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.json.VersionedJson
|
||||||
import ru.dbotthepony.kstarbound.io.readVarInt
|
import ru.dbotthepony.kstarbound.io.readVarInt
|
||||||
import ru.dbotthepony.kstarbound.util.AssetPathStack
|
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.Vector2d
|
||||||
import ru.dbotthepony.kvector.vector.Vector2f
|
|
||||||
import java.io.BufferedInputStream
|
import java.io.BufferedInputStream
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.io.DataInputStream
|
import java.io.DataInputStream
|
||||||
|
@ -236,7 +236,8 @@ class StarboundClient : Closeable {
|
|||||||
putDebugLog("Initialized GLFW window")
|
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)
|
val maxVertexAttribBindPoints = glGetInteger(GL_MAX_VERTEX_ATTRIB_BINDINGS)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -375,6 +376,17 @@ class StarboundClient : Closeable {
|
|||||||
checkForGLError()
|
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) {
|
fun setViewport(x: Int, y: Int, width: Int, height: Int) {
|
||||||
ensureSameThread()
|
ensureSameThread()
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ private class GLTexturePropertyTracker(private val flag: Int, private var value:
|
|||||||
@Suppress("SameParameterValue")
|
@Suppress("SameParameterValue")
|
||||||
class GLTexture2D(val name: String = "<unknown>") : GLObject() {
|
class GLTexture2D(val name: String = "<unknown>") : GLObject() {
|
||||||
override val client = StarboundClient.current()
|
override val client = StarboundClient.current()
|
||||||
override val pointer = glGenTextures()
|
override val pointer = glCreateTextures(GL_TEXTURE_2D)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
checkForGLError()
|
checkForGLError()
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
package ru.dbotthepony.kstarbound.client.gl.shader
|
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.StarboundClient
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
|
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder
|
import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexAttributes
|
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.api.IStruct4f
|
||||||
import ru.dbotthepony.kvector.arrays.Matrix3f
|
import ru.dbotthepony.kvector.arrays.Matrix3f
|
||||||
import ru.dbotthepony.kvector.vector.RGBAColor
|
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"))
|
return listOf(client.internalVertex("shaders/vertex/$name.vsh"), client.internalFragment("shaders/fragment/$name.fsh"))
|
||||||
}
|
}
|
||||||
|
|
||||||
class FontProgram : GLShaderProgram(shaders1("font"), VertexAttributes.POSITION_UV), GLShaderProgram.Regular {
|
private fun fontShaders(): List<GLShader> {
|
||||||
override var viewMatrix: Matrix3f by F3x3Uniform("viewMatrix", true)
|
val client = StarboundClient.current()
|
||||||
override var worldMatrix: Matrix3f by F3x3Uniform("worldMatrix", true)
|
var read = StarboundClient.readInternal("shaders/fragment/font.fsh")
|
||||||
override var modelMatrix: Matrix3f by F3x3Uniform("modelMatrix", true)
|
|
||||||
override var colorMultiplier: IStruct4f by F4Uniform("colorMultiplier", true)
|
|
||||||
var texture by IUniform("texture0")
|
|
||||||
|
|
||||||
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 {
|
init {
|
||||||
viewMatrix = Matrix3f.identity()
|
viewMatrix = Matrix3f.identity()
|
||||||
|
@ -3,7 +3,8 @@ package ru.dbotthepony.kstarbound.client.gl.vertex
|
|||||||
import ru.dbotthepony.kstarbound.client.gl.GLType
|
import ru.dbotthepony.kstarbound.client.gl.GLType
|
||||||
|
|
||||||
enum class VertexAttributeType(val type: GLType) {
|
enum class VertexAttributeType(val type: GLType) {
|
||||||
POSITION(GLType.VEC2F),
|
POSITION(GLType.VEC3F),
|
||||||
|
TEXTURE_INDEX(GLType.FLOAT),
|
||||||
COLOR(GLType.VEC4F),
|
COLOR(GLType.VEC4F),
|
||||||
UV(GLType.VEC2F),
|
UV(GLType.VEC2F),
|
||||||
HUE_SHIFT(GLType.FLOAT)
|
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.kstarbound.client.gl.BufferObject
|
||||||
import ru.dbotthepony.kvector.api.IStruct2f
|
import ru.dbotthepony.kvector.api.IStruct2f
|
||||||
import ru.dbotthepony.kvector.api.IStruct4f
|
import ru.dbotthepony.kvector.api.IStruct4f
|
||||||
|
import ru.dbotthepony.kvector.arrays.Matrix3f
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.nio.ByteOrder
|
import java.nio.ByteOrder
|
||||||
|
|
||||||
@ -191,6 +192,7 @@ class VertexBuilder(
|
|||||||
vertexCount = 0
|
vertexCount = 0
|
||||||
indexCount = 0
|
indexCount = 0
|
||||||
elementCount = 0
|
elementCount = 0
|
||||||
|
elementVertices = 0
|
||||||
vertexMemory.position(0)
|
vertexMemory.position(0)
|
||||||
indexMemory.position(0)
|
indexMemory.position(0)
|
||||||
ensureCapacity()
|
ensureCapacity()
|
||||||
@ -235,11 +237,15 @@ class VertexBuilder(
|
|||||||
elementCount++
|
elementCount++
|
||||||
elementVertices = 0
|
elementVertices = 0
|
||||||
|
|
||||||
|
val indexWriter = indexWriter
|
||||||
|
val indexMemory = indexMemory
|
||||||
|
val elementIndexOffset = elementIndexOffset
|
||||||
|
|
||||||
for (index in mode.indices.indices) {
|
for (index in mode.indices.indices) {
|
||||||
indexWriter.write(indexMemory, mode.indices.getInt(index) + elementIndexOffset)
|
indexWriter.write(indexMemory, mode.indices.getInt(index) + elementIndexOffset)
|
||||||
}
|
}
|
||||||
|
|
||||||
elementIndexOffset += mode.elements
|
this.elementIndexOffset += mode.elements
|
||||||
indexCount += mode.indices.size
|
indexCount += mode.indices.size
|
||||||
|
|
||||||
ensureCapacity()
|
ensureCapacity()
|
||||||
@ -261,6 +267,12 @@ class VertexBuilder(
|
|||||||
return this
|
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 {
|
private fun pushFloat(attr: VertexAttributes.Attribute, x: Float): VertexBuilder {
|
||||||
vertexMemory.position(vertexCount * attributes.vertexStride + attr.relativeOffset)
|
vertexMemory.position(vertexCount * attributes.vertexStride + attr.relativeOffset)
|
||||||
vertexMemory.putFloat(x)
|
vertexMemory.putFloat(x)
|
||||||
@ -269,6 +281,14 @@ class VertexBuilder(
|
|||||||
return this
|
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 {
|
private fun pushFloat2(attr: VertexAttributes.Attribute, x: Float, y: Float): VertexBuilder {
|
||||||
vertexMemory.position(vertexCount * attributes.vertexStride + attr.relativeOffset)
|
vertexMemory.position(vertexCount * attributes.vertexStride + attr.relativeOffset)
|
||||||
vertexMemory.putFloat(x)
|
vertexMemory.putFloat(x)
|
||||||
@ -278,6 +298,16 @@ class VertexBuilder(
|
|||||||
return this
|
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 {
|
private fun pushFloat4(attr: VertexAttributes.Attribute, x: Float, y: Float, z: Float, w: Float): VertexBuilder {
|
||||||
vertexMemory.position(vertexCount * attributes.vertexStride + attr.relativeOffset)
|
vertexMemory.position(vertexCount * attributes.vertexStride + attr.relativeOffset)
|
||||||
vertexMemory.putFloat(x)
|
vertexMemory.putFloat(x)
|
||||||
@ -289,25 +319,45 @@ class VertexBuilder(
|
|||||||
return this
|
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 {
|
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(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 {
|
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 uv(value: IStruct2f) = position(value.component1(), value.component2())
|
||||||
|
|
||||||
fun color(x: Float, y: Float, z: Float, w: Float): VertexBuilder {
|
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 color(value: IStruct4f) = color(value.component1(), value.component2(), value.component3(), value.component4())
|
||||||
|
|
||||||
fun hueShift(x: Float): VertexBuilder {
|
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
|
package ru.dbotthepony.kstarbound.client.render
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.chars.Char2ObjectArrayMap
|
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.Object2ObjectFunction
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet
|
||||||
import org.lwjgl.opengl.GL45.*
|
import org.lwjgl.opengl.GL45.*
|
||||||
|
import org.lwjgl.system.MemoryUtil
|
||||||
import ru.dbotthepony.kstarbound.client.StarboundClient
|
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||||
import ru.dbotthepony.kstarbound.client.freetype.LoadFlag
|
import ru.dbotthepony.kstarbound.client.freetype.LoadFlag
|
||||||
import ru.dbotthepony.kstarbound.client.gl.*
|
import ru.dbotthepony.kstarbound.client.gl.*
|
||||||
import ru.dbotthepony.kstarbound.client.freetype.struct.FT_Pixel_Mode
|
import ru.dbotthepony.kstarbound.client.freetype.struct.FT_Pixel_Mode
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexAttributes
|
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexAttributes
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
|
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.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.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.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> {
|
private fun breakLines(text: String): List<String> {
|
||||||
var nextLineBreak = text.indexOf('\n', 0)
|
var nextLineBreak = text.indexOf('\n', 0)
|
||||||
@ -55,12 +68,14 @@ enum class TextAlignY {
|
|||||||
TOP, CENTER, BOTTOM
|
TOP, CENTER, BOTTOM
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val FONT_VERTEX_FORMAT = VertexAttributes.of(VertexAttributeType.POSITION, VertexAttributeType.UV)
|
||||||
|
|
||||||
class Font(
|
class Font(
|
||||||
val font: String = "hobo.ttf",
|
val font: String = "hobo.ttf",
|
||||||
val size: Int = 48
|
val size: Int = 48
|
||||||
) {
|
) {
|
||||||
val state = StarboundClient.current()
|
val client = StarboundClient.current()
|
||||||
val face = state.freeType.Face(font, 0L)
|
val face = client.freeType.Face(font, 0L)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
face.setSize(0, size)
|
face.setSize(0, size)
|
||||||
@ -78,6 +93,37 @@ class Font(
|
|||||||
private val descender = face.nativeMemory.descender.toFloat() / ((size / 48f) * 32f)
|
private val descender = face.nativeMemory.descender.toFloat() / ((size / 48f) * 32f)
|
||||||
val lineHeight = ascender - descender
|
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(
|
fun render(
|
||||||
text: List<String>,
|
text: List<String>,
|
||||||
|
|
||||||
@ -111,11 +157,7 @@ class Font(
|
|||||||
if (scale != 1f)
|
if (scale != 1f)
|
||||||
model.scale(x = scale, y = scale)
|
model.scale(x = scale, y = scale)
|
||||||
|
|
||||||
state.programs.font.use()
|
builder.begin()
|
||||||
state.programs.font.colorMultiplier = color
|
|
||||||
state.programs.font.worldMatrix = state.stack.last()
|
|
||||||
state.activeTexture = 0
|
|
||||||
state.programs.font.texture = 0
|
|
||||||
|
|
||||||
val space = getGlyph(' ')
|
val space = getGlyph(' ')
|
||||||
|
|
||||||
@ -170,7 +212,19 @@ class Font(
|
|||||||
model.translate(x = -lineWidth - movedX, y = lineHeight)
|
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)
|
return TextSize(totalX * scale, totalY * scale)
|
||||||
}
|
}
|
||||||
@ -207,10 +261,10 @@ class Font(
|
|||||||
if (chr == '\t') {
|
if (chr == '\t') {
|
||||||
if (lineGlyphs % 4 == 0) {
|
if (lineGlyphs % 4 == 0) {
|
||||||
lineWidth += space.advanceX * 4
|
lineWidth += space.advanceX * 4
|
||||||
state.stack.last().translate(x = space.advanceX * 4)
|
client.stack.last().translate(x = space.advanceX * 4)
|
||||||
} else {
|
} else {
|
||||||
lineWidth += space.advanceX * (lineGlyphs % 4)
|
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
|
lineGlyphs += lineGlyphs % 4
|
||||||
@ -243,8 +297,6 @@ class Font(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private inner class Glyph(val char: Char) {
|
private inner class Glyph(val char: Char) {
|
||||||
private val texture: GLTexture2D?
|
|
||||||
|
|
||||||
val isEmpty: Boolean
|
val isEmpty: Boolean
|
||||||
|
|
||||||
val width: Float
|
val width: Float
|
||||||
@ -255,13 +307,13 @@ class Font(
|
|||||||
val advanceX: Float
|
val advanceX: Float
|
||||||
val advanceY: Float
|
val advanceY: Float
|
||||||
|
|
||||||
private val vao: VertexArrayObject?
|
val uv: Vector4f
|
||||||
|
|
||||||
private val indexCount: Int
|
|
||||||
|
|
||||||
private val elementIndexType: Int
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
if (nextAtlasY + bbox.height > atlasHeight) {
|
||||||
|
throw IllegalStateException("Font atlas is full")
|
||||||
|
}
|
||||||
|
|
||||||
face.loadChar(char, LoadFlag.RENDER)
|
face.loadChar(char, LoadFlag.RENDER)
|
||||||
val glyph = face.nativeMemory.glyph ?: throw IllegalStateException("Unable to load glyph data for $char (code ${char.code})")
|
val glyph = face.nativeMemory.glyph ?: throw IllegalStateException("Unable to load glyph data for $char (code ${char.code})")
|
||||||
val bitmap = glyph.bitmap
|
val bitmap = glyph.bitmap
|
||||||
@ -282,43 +334,26 @@ class Font(
|
|||||||
// интересно, а что будет когда буфер будет "вычищен" из памяти? Чо, будет double free?
|
// интересно, а что будет когда буфер будет "вычищен" из памяти? Чо, будет double free?
|
||||||
val buff = bitmap.byteBuffer!!
|
val buff = bitmap.byteBuffer!!
|
||||||
|
|
||||||
texture = state.newTexture("$font:$char")
|
|
||||||
|
|
||||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
|
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
|
try {
|
||||||
texture.textureMagFilter = GL_LINEAR
|
uv = Vector4f(nextAtlasX / atlasWidth.toFloat(), nextAtlasY / atlasHeight.toFloat(), (nextAtlasX + bitmap.width) / atlasWidth.toFloat(), (nextAtlasY + bitmap.rows) / atlasHeight.toFloat())
|
||||||
texture.textureWrapS = GL_CLAMP_TO_EDGE
|
|
||||||
texture.textureWrapT = GL_CLAMP_TO_EDGE
|
|
||||||
|
|
||||||
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()
|
nextAtlasX += bbox.width
|
||||||
val ebo = state.newEBO()
|
|
||||||
val vbo = state.newVBO()
|
|
||||||
|
|
||||||
vao.elementBuffer = ebo
|
if (nextAtlasX + bbox.width > atlasWidth) {
|
||||||
vao.bindAttributes(vbo, VertexAttributes.POSITION_UV)
|
nextAtlasX = 0
|
||||||
|
nextAtlasY += bbox.height
|
||||||
val builder = VertexBuilder(VertexAttributes.POSITION_UV, GeometryType.QUADS)
|
}
|
||||||
|
} finally {
|
||||||
builder.vertex(0f, 0f).uv(0f, 0f)
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 4)
|
||||||
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
|
|
||||||
} else {
|
} else {
|
||||||
isEmpty = true
|
isEmpty = true
|
||||||
indexCount = 0
|
uv = Vector4f.ZERO
|
||||||
elementIndexType = 0
|
|
||||||
|
|
||||||
vao = null
|
|
||||||
texture = null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -328,14 +363,12 @@ class Font(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
vao!!.bind()
|
|
||||||
|
|
||||||
model.translate(bearingX, -bearingY)
|
model.translate(bearingX, -bearingY)
|
||||||
|
|
||||||
texture!!.bind()
|
builder.vertex(model, 0f, 0f).uv(uv.x, uv.y)
|
||||||
state.programs.font.modelMatrix = model
|
builder.vertex(model, width, 0f).uv(uv.z, uv.y)
|
||||||
glDrawElements(GL_TRIANGLES, indexCount, elementIndexType, 0L)
|
builder.vertex(model, width, height).uv(uv.z, uv.w)
|
||||||
checkForGLError()
|
builder.vertex(model, 0f, height).uv(uv.x, uv.w)
|
||||||
|
|
||||||
model.translate(advanceX - bearingX, bearingY)
|
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) {
|
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(RenderLayer.Object.index) {
|
||||||
layers.add(obj.orientation?.renderLayer ?: continue) {
|
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(), 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())
|
||||||
it.vertex(obj.pos.x.toFloat() + 1f, obj.pos.y.toFloat() + 1f)
|
it.vertex(obj.pos.x.toFloat() + 1f, obj.pos.y.toFloat() + 1f)
|
||||||
it.vertex(obj.pos.x.toFloat(), obj.pos.y.toFloat() + 1f)
|
it.vertex(obj.pos.x.toFloat(), obj.pos.y.toFloat() + 1f)
|
||||||
}
|
}*/
|
||||||
|
|
||||||
obj.drawables.forEach {
|
obj.drawables.forEach {
|
||||||
val (x, y) = obj.imagePosition
|
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 texture = client.loadTexture(path.imagePath.value!!)
|
||||||
val program = if (fullbright) client.programs.positionTexture else client.programs.positionTextureLightmap
|
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()
|
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) })
|
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
|
mat
|
||||||
})
|
})
|
||||||
|
|
||||||
|
//program.modelMatrix = mat
|
||||||
|
program.modelMatrix = Matrix3f.identity()
|
||||||
|
|
||||||
program.worldMatrix = client.stack.last()
|
program.worldMatrix = client.stack.last()
|
||||||
program.use()
|
program.use()
|
||||||
program.texture0 = 0
|
program.texture0 = 0
|
||||||
@ -112,10 +115,10 @@ sealed class Drawable(val position: Vector2f, val color: RGBAColor, val fullbrig
|
|||||||
texture.bind()
|
texture.bind()
|
||||||
|
|
||||||
program.builder.builder.begin(GeometryType.QUADS)
|
program.builder.builder.begin(GeometryType.QUADS)
|
||||||
program.builder.builder.vertex(x, y).uv(sprite.u0, sprite.v0)
|
program.builder.builder.vertex(mat, x, y).uv(sprite.u0, sprite.v0)
|
||||||
program.builder.builder.vertex(x + sprite.width, y).uv(sprite.u1, sprite.v0)
|
program.builder.builder.vertex(mat, 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(mat, 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 + sprite.height).uv(sprite.u0, sprite.v1)
|
||||||
program.builder.upload()
|
program.builder.upload()
|
||||||
program.builder.draw()
|
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.IChunkCell
|
||||||
import ru.dbotthepony.kstarbound.world.api.TileView
|
import ru.dbotthepony.kstarbound.world.api.TileView
|
||||||
import ru.dbotthepony.kstarbound.world.entities.Entity
|
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.IStruct2d
|
||||||
import ru.dbotthepony.kvector.api.IStruct2i
|
import ru.dbotthepony.kvector.api.IStruct2i
|
||||||
import ru.dbotthepony.kvector.arrays.Object2DArray
|
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.kstarbound.world.World
|
||||||
import ru.dbotthepony.kvector.vector.Vector2d
|
import ru.dbotthepony.kvector.vector.Vector2d
|
||||||
|
|
||||||
abstract class Entity(
|
abstract class Entity(val world: World<*, *>) {
|
||||||
/**
|
|
||||||
* The world this entity in, never changes.
|
|
||||||
*/
|
|
||||||
val world: World<*, *>
|
|
||||||
) {
|
|
||||||
/**
|
/**
|
||||||
* The chunk this entity currently in, it is automatically updated on each change of [position]
|
* The chunk this entity currently in, it is automatically updated on each change of [position]
|
||||||
*/
|
*/
|
||||||
@ -17,13 +12,9 @@ abstract class Entity(
|
|||||||
set(value) {
|
set(value) {
|
||||||
if (!isSpawned) {
|
if (!isSpawned) {
|
||||||
throw IllegalStateException("Trying to set chunk this entity belong to before spawning in world")
|
throw IllegalStateException("Trying to set chunk this entity belong to before spawning in world")
|
||||||
}
|
} else if (isRemoved) {
|
||||||
|
|
||||||
if (isRemoved) {
|
|
||||||
throw IllegalStateException("This entity was removed")
|
throw IllegalStateException("This entity was removed")
|
||||||
}
|
} else if (value == field) {
|
||||||
|
|
||||||
if (value == field) {
|
|
||||||
return
|
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.common.collect.ImmutableMap
|
||||||
import com.google.gson.JsonObject
|
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;
|
uniform vec4 colorMultiplier;
|
||||||
out vec4 colorResult;
|
out vec4 colorResult;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
layout (location = 0) in vec2 pos;
|
layout (location = 0) in vec3 pos;
|
||||||
|
|
||||||
#ifdef TEXTURE
|
#ifdef TEXTURE
|
||||||
layout (location = TEXTURE) in vec2 uvIn;
|
layout (location = TEXTURE) in vec2 uvIn;
|
||||||
@ -21,7 +21,7 @@ uniform mat3 worldMatrix; // matrix stack
|
|||||||
uniform mat3 modelMatrix; // local transformations
|
uniform mat3 modelMatrix; // local transformations
|
||||||
|
|
||||||
void main() {
|
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);
|
gl_Position = vec4(result.x, result.y, 0.0, result.z);
|
||||||
|
|
||||||
#ifdef HUE_SHIFT
|
#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;
|
layout (location = 1) in vec2 uvData;
|
||||||
|
|
||||||
uniform mat3 viewMatrix; // projection (viewport)
|
uniform mat3 viewMatrix; // projection (viewport)
|
||||||
uniform mat3 worldMatrix; // world position
|
uniform mat3 worldMatrix; // world position
|
||||||
uniform mat3 modelMatrix; // mesh/local transformations
|
|
||||||
|
|
||||||
out vec2 uvOut;
|
out vec2 uvOut;
|
||||||
|
|
||||||
void main() {
|
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);
|
gl_Position = vec4(result.x, result.y, 0.0, result.z);
|
||||||
|
|
||||||
uvOut = uvData;
|
uvOut = uvData;
|
||||||
|
Loading…
Reference in New Issue
Block a user