diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.kt index ea9ed1b99..b3e2080cd 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.kt @@ -2,8 +2,12 @@ package de.bixilon.minosoft.gui.rendering import de.bixilon.minosoft.data.entities.EntityRotation import de.bixilon.minosoft.data.entities.Location +import de.bixilon.minosoft.data.mappings.blocks.Block import de.bixilon.minosoft.data.world.ChunkLocation +import de.bixilon.minosoft.gui.rendering.font.Font +import de.bixilon.minosoft.gui.rendering.font.Font2DMesh import de.bixilon.minosoft.gui.rendering.shader.Shader +import de.bixilon.minosoft.gui.rendering.textures.Texture import de.bixilon.minosoft.gui.rendering.textures.TextureArray import de.bixilon.minosoft.protocol.network.Connection import de.bixilon.minosoft.protocol.packets.serverbound.play.PacketPlayerPositionAndRotationSending @@ -27,7 +31,9 @@ class RenderWindow(private val connection: Connection) { private var screenHeight = 600 private var polygonEnabled = false private lateinit var chunkShader: Shader + private lateinit var fontShader: Shader private lateinit var minecraftTextures: TextureArray + private lateinit var fontAtlasTexture: TextureArray private var windowId: Long = 0 private var deltaTime = 0.0 // time between current frame and last frame @@ -36,7 +42,8 @@ class RenderWindow(private val connection: Connection) { val renderQueue = ConcurrentLinkedQueue() - val chunkSectionsToDraw = ConcurrentHashMap>() + val chunkSectionsToDraw = ConcurrentHashMap>() + val font2DToDraw = ConcurrentLinkedQueue() fun init(latch: CountUpAndDownLatch? = null) { @@ -110,17 +117,35 @@ class RenderWindow(private val connection: Connection) { glEnable(GL_BLEND) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - minecraftTextures = TextureArray(connection.version.assetsManager, connection.version.mapping.blockMap.values) + minecraftTextures = TextureArray(connection.version.assetsManager, resolveBlockTextureIds(connection.version.mapping.blockMap.values)) minecraftTextures.load() + fontAtlasTexture = TextureArray(connection.version.assetsManager, listOf(Texture("font/unicode_page_00", 0)), 256) + fontAtlasTexture.load() chunkShader = Shader("chunk_vertex.glsl", "chunk_fragment.glsl") chunkShader.load() chunkShader.use() - chunkShader.setInt("texture0", 0) camera.calculateProjectionMatrix(screenWidth, screenHeight, chunkShader) camera.calculateViewMatrix(chunkShader) + fontShader = Shader("font_vertex.glsl", "font_fragment.glsl") + fontShader.load() + fontShader.use() + fontShader.setFloat("atlasSize", 256f) + + val font = Font() + val char = font.chars['ยง']!! + font2DToDraw.add(Font2DMesh(floatArrayOf( + -0.5f, -0.5f, char.column * 16f, char.row * 16f + char.width, font.atlasOffset + char.atlasPage.toFloat(), + -0.5f, 0f, char.column * 16f, char.row * 16f, font.atlasOffset + char.atlasPage.toFloat(), + 0f, -0.5f, char.column * 16f + char.width, char.row * 16f + char.width, font.atlasOffset + char.atlasPage.toFloat(), + 0f, -0.5f, char.column * 16f + char.width, char.row * 16f + char.width, font.atlasOffset + char.atlasPage.toFloat(), + -0.5f, 0f, char.column * 16f, char.row * 16f, font.atlasOffset + char.atlasPage.toFloat(), + 0f, 0f, char.column * 16f + char.width, char.row * 16f, font.atlasOffset + char.atlasPage.toFloat(), + ))) + + glfwSetWindowSizeCallback(windowId, object : GLFWWindowSizeCallback() { override fun invoke(window: Long, width: Int, height: Int) { glViewport(0, 0, width, height) @@ -148,6 +173,8 @@ class RenderWindow(private val connection: Connection) { deltaTime = currentFrame - lastFrame lastFrame = currentFrame + glEnable(GL_DEPTH_TEST) + minecraftTextures.use(GL_TEXTURE0) chunkShader.use() @@ -160,6 +187,15 @@ class RenderWindow(private val connection: Connection) { } } + glDisable(GL_DEPTH_TEST) + fontAtlasTexture.use(GL_TEXTURE0) + + fontShader.use() + + for (font in font2DToDraw) { + font.draw() + } + glfwSwapBuffers(windowId) glfwPollEvents() @@ -179,6 +215,7 @@ class RenderWindow(private val connection: Connection) { if (glfwGetTime() - lastPositionChangeTime > 0.05) { // ToDo: Replace this with proper movement and only send it, when out position changed connection.sendPacket(PacketPlayerPositionAndRotationSending(Location(camera.cameraPosition), EntityRotation(camera.yaw, camera.pitch), false)) + lastPositionChangeTime = glfwGetTime() } @@ -207,4 +244,16 @@ class RenderWindow(private val connection: Connection) { }) polygonEnabled = !polygonEnabled } + + private fun resolveBlockTextureIds(blocks: Set): List { + val textures: MutableList = mutableListOf() + textures.add(TextureArray.DEBUG_TEXTURE) + val textureMap: MutableMap = mutableMapOf() + textureMap[TextureArray.DEBUG_TEXTURE.name] = TextureArray.DEBUG_TEXTURE + + for (block in blocks) { + block.blockModel?.resolveTextures(textures, textureMap) + } + return textures + } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/Renderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/Renderer.kt index 40ac0df21..bf9ebd83f 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/Renderer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/Renderer.kt @@ -45,7 +45,7 @@ class Renderer(private val connection: Connection) { renderWindow.renderQueue.add { sectionMap[sectionHeight]?.unload() sectionMap.remove(sectionHeight) - sectionMap[sectionHeight] = Mesh(data, Vec3(chunkLocation.x, sectionHeight, chunkLocation.z)) + sectionMap[sectionHeight] = WorldMesh(data, Vec3(chunkLocation.x, sectionHeight, chunkLocation.z)) } } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/Mesh.java b/src/main/java/de/bixilon/minosoft/gui/rendering/WorldMesh.java similarity index 63% rename from src/main/java/de/bixilon/minosoft/gui/rendering/Mesh.java rename to src/main/java/de/bixilon/minosoft/gui/rendering/WorldMesh.java index d7084fe1e..dd76d5105 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/Mesh.java +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/WorldMesh.java @@ -2,9 +2,6 @@ package de.bixilon.minosoft.gui.rendering; import de.bixilon.minosoft.gui.rendering.shader.Shader; import glm_.vec3.Vec3; -import org.lwjgl.opengl.GL11; -import org.lwjgl.opengl.GL15; -import org.lwjgl.opengl.GL20; import static org.lwjgl.opengl.GL11.GL_TRIANGLES; import static org.lwjgl.opengl.GL11.glDrawArrays; @@ -12,13 +9,13 @@ import static org.lwjgl.opengl.GL15.glDeleteBuffers; import static org.lwjgl.opengl.GL15.glGenBuffers; import static org.lwjgl.opengl.GL30.*; -public class Mesh { +public class WorldMesh { int vAO; int vBO; Vec3 worldPosition; int trianglesCount; - public Mesh(float[] data, Vec3 worldPosition) { + public WorldMesh(float[] data, Vec3 worldPosition) { this.worldPosition = worldPosition; this.trianglesCount = data.length / 6; // <- bytes per vertex this.vAO = glGenVertexArrays(); @@ -26,17 +23,17 @@ public class Mesh { // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s). glBindVertexArray(this.vAO); - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, this.vBO); - GL15.glBufferData(GL15.GL_ARRAY_BUFFER, data, GL15.GL_STATIC_DRAW); - GL20.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 6 * Float.BYTES, 0L); - GL20.glEnableVertexAttribArray(0); - GL20.glVertexAttribPointer(1, 2, GL11.GL_FLOAT, false, 6 * Float.BYTES, 3 * Float.BYTES); - GL20.glEnableVertexAttribArray(1); - GL20.glVertexAttribPointer(2, 1, GL11.GL_FLOAT, false, 6 * Float.BYTES, 5 * Float.BYTES); - GL20.glEnableVertexAttribArray(2); + glBindBuffer(GL_ARRAY_BUFFER, this.vBO); + glBufferData(GL_ARRAY_BUFFER, data, GL_STATIC_DRAW); + glVertexAttribPointer(0, 3, GL_FLOAT, false, 6 * Float.BYTES, 0L); + glEnableVertexAttribArray(0); + glVertexAttribPointer(1, 2, GL_FLOAT, false, 6 * Float.BYTES, 3 * Float.BYTES); + glEnableVertexAttribArray(1); + glVertexAttribPointer(2, 1, GL_FLOAT, false, 6 * Float.BYTES, 5 * Float.BYTES); + glEnableVertexAttribArray(2); // note that this is allowed, the call to glVertexAttribPointer registered VBO as the vertex attribute's bound vertex buffer object so afterwards we can safely unbind - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); } public void draw(Shader chunkShader) { diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/font/Font.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/font/Font.kt new file mode 100644 index 000000000..2f0f66912 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/font/Font.kt @@ -0,0 +1,17 @@ +package de.bixilon.minosoft.gui.rendering.font + + +class Font { + val chars: MutableMap = mutableMapOf() + val atlasOffset = 0 + + init { + for (page in 0 until 256) { + for (x in 0 until 16) { + for (y in 0 until 16) { + chars[(page * 256 + (x * 16 + y)).toChar()] = FontChar(page, x, y) + } + } + } + } +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/font/Font2DMesh.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/font/Font2DMesh.kt new file mode 100644 index 000000000..c30d12372 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/font/Font2DMesh.kt @@ -0,0 +1,40 @@ +package de.bixilon.minosoft.gui.rendering.font + +import glm_.BYTES +import org.lwjgl.opengl.GL11.GL_FLOAT +import org.lwjgl.opengl.GL20.glEnableVertexAttribArray +import org.lwjgl.opengl.GL20.glVertexAttribPointer +import org.lwjgl.opengl.GL30.* + +class Font2DMesh(data: FloatArray) { + var vAO: Int = glGenVertexArrays() + var vBO: Int = glGenBuffers() + var trianglesCount: Int = data.size / 5 // <- bytes per vertex + + fun draw() { + glBindVertexArray(vAO) + glDrawArrays(GL_TRIANGLES, 0, trianglesCount) + } + + fun unload() { + glDeleteVertexArrays(vAO) + glDeleteBuffers(vBO) + } + + init { + + // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s). + glBindVertexArray(vAO) + glBindBuffer(GL_ARRAY_BUFFER, vBO) + glBufferData(GL_ARRAY_BUFFER, data, GL_STATIC_DRAW) + glVertexAttribPointer(0, 2, GL_FLOAT, false, 5 * Float.BYTES, 0L) + glEnableVertexAttribArray(0) + glVertexAttribPointer(1, 2, GL_FLOAT, false, 5 * Float.BYTES, (2 * Float.BYTES).toLong()) + glEnableVertexAttribArray(1) + glVertexAttribPointer(2, 1, GL_FLOAT, false, 5 * Float.BYTES, (4 * Float.BYTES).toLong()) + glEnableVertexAttribArray(2) + + // note that this is allowed, the call to glVertexAttribPointer registered VBO as the vertex attribute's bound vertex buffer object so afterwards we can safely unbind + glBindBuffer(GL_ARRAY_BUFFER, 0) + } +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/font/FontChar.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/font/FontChar.kt new file mode 100644 index 000000000..0cfa59685 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/font/FontChar.kt @@ -0,0 +1,6 @@ +package de.bixilon.minosoft.gui.rendering.font + +class FontChar(val atlasPage: Int, val row: Int, val column: Int) { + val width = 16 + +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/hud/HUDScale.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/hud/HUDScale.kt new file mode 100644 index 000000000..3de1acb8c --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/hud/HUDScale.kt @@ -0,0 +1,8 @@ +package de.bixilon.minosoft.gui.rendering.hud + +enum class HUDScale(val scale: Float) { + TINY(0.1f), + MEDIUM(0.2f), + LARGE(0.5f), + SENIOR(1.0f), +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/textures/TextureArray.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/textures/TextureArray.kt index c35b9184a..1fa24d405 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/textures/TextureArray.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/textures/TextureArray.kt @@ -1,8 +1,6 @@ package de.bixilon.minosoft.gui.rendering.textures import de.bixilon.minosoft.data.assets.AssetsManager -import de.bixilon.minosoft.data.mappings.blocks.Block -import de.bixilon.minosoft.gui.rendering.textures.TextureLoader.loadTextureArray import org.lwjgl.opengl.GL11.* import org.lwjgl.opengl.GL12.glTexImage3D import org.lwjgl.opengl.GL12.glTexSubImage3D @@ -11,7 +9,7 @@ import org.lwjgl.opengl.GL30.GL_TEXTURE_2D_ARRAY import org.lwjgl.opengl.GL30.glGenerateMipmap import java.nio.ByteBuffer -class TextureArray(private val assetsManager: AssetsManager, private val blocks: Set) { +class TextureArray(private val assetsManager: AssetsManager, private val textures: List, private val size: Int = 16) { var textureId = 0 fun load(): Int { @@ -24,32 +22,20 @@ class TextureArray(private val assetsManager: AssetsManager, private val blocks: // load and generate the texture - val textures = resolveTextureIds(blocks) - val textureMap: Map = loadTextureArray(assetsManager, textures) - glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, 16, 16, textures.size, 0, GL_RGBA, GL_UNSIGNED_BYTE, null as ByteBuffer?) + val textureMap: Map = TextureLoader.loadTextureArray(assetsManager, textures) + glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, size, size, textures.size, 0, GL_RGBA, GL_UNSIGNED_BYTE, null as ByteBuffer?) for ((key, value) in textureMap) { - glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, key.id, 16, 16, 1, GL_RGBA, GL_UNSIGNED_BYTE, value) + glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, key.id, size, size, 1, GL_RGBA, GL_UNSIGNED_BYTE, value) } glGenerateMipmap(GL_TEXTURE_2D_ARRAY) return textureId } - private fun resolveTextureIds(blocks: Set): List { - val textures: MutableList = mutableListOf() - textures.add(DEBUG_TEXTURE) - val textureMap: MutableMap = mutableMapOf() - textureMap[DEBUG_TEXTURE.name] = DEBUG_TEXTURE - - for (block in blocks) { - block.blockModel?.resolveTextures(textures, textureMap) - } - return textures - } fun use(textureMode: Int) { glActiveTexture(textureMode) - glBindTexture(GL_TEXTURE_2D, textureId) + glBindTexture(GL_TEXTURE_2D_ARRAY, textureId) } companion object { diff --git a/src/main/resources/assets/rendering/shader/chunk_vertex.glsl b/src/main/resources/assets/rendering/shader/chunk_vertex.glsl index b7074cba1..559c103cd 100644 --- a/src/main/resources/assets/rendering/shader/chunk_vertex.glsl +++ b/src/main/resources/assets/rendering/shader/chunk_vertex.glsl @@ -3,7 +3,6 @@ layout (location = 0) in vec3 inPosition; layout (location = 1) in vec2 textureIndex; layout (location = 2) in float textureLayer; -out vec3 vertexColor; out vec3 passTextureCoordinates; diff --git a/src/main/resources/assets/rendering/shader/font_fragment.glsl b/src/main/resources/assets/rendering/shader/font_fragment.glsl new file mode 100644 index 000000000..9ffa5e269 --- /dev/null +++ b/src/main/resources/assets/rendering/shader/font_fragment.glsl @@ -0,0 +1,19 @@ +#version 330 core + +out vec4 outColor; + +in vec3 passTextureCoordinates; + +uniform sampler2DArray texureArray; + + +void main() { + vec4 texColor = texture(texureArray, passTextureCoordinates); + if (texColor.a == 0) { + discard; + } + texColor.r = 0.8f; + texColor.g = 0.8f; + texColor.b = 0.0f; + outColor = texColor; +} diff --git a/src/main/resources/assets/rendering/shader/font_vertex.glsl b/src/main/resources/assets/rendering/shader/font_vertex.glsl new file mode 100644 index 000000000..dc1c2bd8a --- /dev/null +++ b/src/main/resources/assets/rendering/shader/font_vertex.glsl @@ -0,0 +1,13 @@ +#version 330 core +layout (location = 0) in vec2 inPosition; +layout (location = 1) in vec2 textureIndex; +layout (location = 2) in float textureLayer; + +out vec3 passTextureCoordinates; + +uniform float atlasSize; + +void main() { + gl_Position = vec4(inPosition, 0.0f, 1.0f); + passTextureCoordinates = vec3(textureIndex / atlasSize, textureLayer); +}