summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrom <romangraef@gmail.com>2021-04-24 03:19:22 +0200
committerrom <romangraef@gmail.com>2021-04-24 11:08:22 +0200
commitec934bf2f0f3536c1b4d31b4ca002f6f38ada9fe (patch)
tree21e44e889c91eeecc1537e69eae7d0b50d25804a
parenta0a40e9259b9e8be4e677c8c29dfdfb89eb81ce8 (diff)
downloadtxtgameengine-ec934bf2f0f3536c1b4d31b4ca002f6f38ada9fe.tar.gz
txtgameengine-ec934bf2f0f3536c1b4d31b4ca002f6f38ada9fe.tar.bz2
txtgameengine-ec934bf2f0f3536c1b4d31b4ca002f6f38ada9fe.zip
font rendering
-rw-r--r--txtgameengine/__main__.py12
-rw-r--r--txtgameengine/builtin_res/shaders/font/fragment.glsl12
-rw-r--r--txtgameengine/builtin_res/shaders/font/vertex.glsl12
-rw-r--r--txtgameengine/fonts.py61
-rw-r--r--txtgameengine/platform.py13
-rw-r--r--txtgameengine/shaders/__init__.py2
-rw-r--r--txtgameengine/shaders/integrated.py6
7 files changed, 106 insertions, 12 deletions
diff --git a/txtgameengine/__main__.py b/txtgameengine/__main__.py
index 71085e1..7aaa64f 100644
--- a/txtgameengine/__main__.py
+++ b/txtgameengine/__main__.py
@@ -1,5 +1,6 @@
import numpy as np
+from .fonts import TextRenderer, BitmapFont
from .scenes import SceneTxtGameApp, Scene
from pathlib import Path
from .shaders import TextureShader
@@ -54,20 +55,23 @@ class TextureScene(Scene):
-1.0, 1.0,
1.0, 1.0,
-1.0, -1.0,
+ 1.0, -1.0,
], np.float32))
self.uvs = self.app.render.setup_buffer(
np.array([
0, 0,
1, 0,
0, 1,
+ 1, 1,
], np.float32))
+ self.text_renderer = TextRenderer(self.app)
+ self.text_renderer.use_font(BitmapFont.fira_mono(self.app))
def update(self, delta: float):
- print(self.app.coords.from_pixels_to_screen(0, 0))
- print(self.app.coords.from_screen_to_pixels(0, 0))
with self.texture_shaders:
- self.app.render.textured_triangle(self.texture_shaders.textureSampler, self.texture, self.triangle,
- self.uvs)
+ self.app.render.bind_texture(self.texture_shaders.textureSampler, self.texture)
+ self.app.render.textured_triangle(self.triangle, self.uvs, 4)
+ self.text_renderer.render_text(0, 0, "HEE")
class TestApp(SceneTxtGameApp):
diff --git a/txtgameengine/builtin_res/shaders/font/fragment.glsl b/txtgameengine/builtin_res/shaders/font/fragment.glsl
new file mode 100644
index 0000000..d9d36fd
--- /dev/null
+++ b/txtgameengine/builtin_res/shaders/font/fragment.glsl
@@ -0,0 +1,12 @@
+#version 330 core
+
+in vec2 UV;
+
+out vec3 color;
+
+uniform sampler2D textureSampler;
+
+void main() {
+ color = texture(textureSampler, UV).rgb;
+}
+
diff --git a/txtgameengine/builtin_res/shaders/font/vertex.glsl b/txtgameengine/builtin_res/shaders/font/vertex.glsl
new file mode 100644
index 0000000..dd93afb
--- /dev/null
+++ b/txtgameengine/builtin_res/shaders/font/vertex.glsl
@@ -0,0 +1,12 @@
+#version 330 core
+
+layout(location=0) in vec3 vertexPosition_modelspace;
+layout(location=1) in vec2 vertexUV;
+
+out vec2 UV;
+
+void main() {
+ gl_Position = vec4(vertexPosition_modelspace, 1);
+ UV = vertexUV;
+}
+
diff --git a/txtgameengine/fonts.py b/txtgameengine/fonts.py
index fcbf8b5..afa9d87 100644
--- a/txtgameengine/fonts.py
+++ b/txtgameengine/fonts.py
@@ -5,12 +5,16 @@ from typing import Optional
import typing
import xml.dom.minidom as minidom
+from .shaders import FontShader
+
if typing.TYPE_CHECKING:
from .twod import Texture
from .app import TxtGameApp
class Font(ABC):
+ texture: 'Texture'
+
def __init__(self):
self.glyphs = dict()
@@ -31,14 +35,15 @@ class Glyph:
class BitmapFont(Font):
- def __init__(self, texture: Texture, xml_path: str):
+ def __init__(self, texture: 'Texture', xml_path: str):
+ super().__init__()
self.texture = texture
- dom = minidom.parse(xml_path)
+ dom = minidom.parse(str(xml_path))
for char in dom.getElementsByTagName('Char'):
code = char.attributes['code'].value
width = char.attributes['width'].value
x_render_offset, y_render_offset = char.attributes['offset'].value.split(' ')
- x_texture_offset, y_texture_offset, x_texture_width, y_texture_width = char.attributes['rect'].split(' ')
+ x_texture_offset, y_texture_offset, x_texture_width, y_texture_width = char.attributes['rect'].value.split(' ')
self.glyphs[code] = \
Glyph(self, int(x_texture_offset), int(y_texture_offset), int(x_texture_width), int(y_texture_width),
int(x_render_offset), int(y_render_offset), int(width))
@@ -48,10 +53,60 @@ class BitmapFont(Font):
from .twod import Texture
return cls(Texture(app, image_path), xml_path)
+ @classmethod
+ def fira_mono(cls, app: 'TxtGameApp'):
+ from .app import builtin_resource_path
+ return cls.load(app, builtin_resource_path / 'fonts/fira_code/regular.png',
+ builtin_resource_path / 'fonts/fira_code/regular.xml')
+
class TextRenderer:
def __init__(self, app: 'TxtGameApp'):
self.app = app
+ self.font: Font = None
+ self.shader = FontShader(app)
def use_font(self, font: Font):
self.font = font
+
+ def render_text(self, x: int, y: int, text: str) -> typing.Tuple[int, int]:
+ """No support for newlines"""
+ if self.font is None:
+ raise ValueError("No font set")
+ glyphs = list(map(self.font.get_glyph, text))
+ missing = [t for t, g in zip(text, glyphs) if g is None]
+ if missing:
+ raise ValueError("No glyph found for character '%s'" % missing[0])
+ with self.shader:
+ self.app.render.bind_texture(self.shader.textureSampler, self.font.texture)
+ for glyph in glyphs:
+ x, y = self._render_glyph(glyph, x, y)
+ return x, y
+
+ def _render_glyph(self, glyph: Glyph, x: int, y: int) -> typing.Tuple[int, int]:
+ low_x = x + glyph.x_render_offset
+ high_x = low_x + glyph.x_texture_width
+ low_y = y + glyph.y_render_offset
+ high_y = low_y + glyph.y_texture_width
+ low_x, low_y = self.app.coords.from_pixels_to_screen(low_x, low_y)
+ high_x, high_y = self.app.coords.from_pixels_to_screen(high_x, high_y)
+ tex_low_x = glyph.x_texture_offset
+ tex_high_x = tex_low_x + glyph.x_texture_width
+ tex_low_y = glyph.y_texture_offset
+ tex_high_y = tex_low_y + glyph.y_texture_width
+ tex_low_x, tex_low_y = self.app.coords.from_pixels_to_screen(tex_low_x, tex_low_y)
+ tex_high_x, tex_high_y = self.app.coords.from_pixels_to_screen(tex_high_x, tex_high_y)
+ render = self.app.render.setup_buffer([
+ low_x, low_y,
+ high_x, low_y,
+ low_x, high_y,
+ high_x, high_y,
+ ])
+ uvs = self.app.render.setup_buffer([
+ tex_low_x, tex_low_y,
+ tex_high_x, tex_low_y,
+ tex_low_x, tex_high_y,
+ tex_high_x, tex_high_y,
+ ])
+ self.app.render.textured_triangle(render, uvs, 4)
+ return x + glyph.x_advance, y
diff --git a/txtgameengine/platform.py b/txtgameengine/platform.py
index 8258d33..bcf3d10 100644
--- a/txtgameengine/platform.py
+++ b/txtgameengine/platform.py
@@ -149,6 +149,8 @@ class RenderComponent:
@staticmethod
def setup_buffer(arr, mode=GL_STATIC_DRAW):
+ if not hasattr(arr, 'itemsize'):
+ arr = np.array(arr, np.float32)
buf = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, buf)
glBufferData(GL_ARRAY_BUFFER, arr.itemsize *
@@ -156,21 +158,24 @@ class RenderComponent:
return buf
@staticmethod
- def triangle(buf):
+ def triangle(buf, count=3):
glEnableVertexAttribArray(0)
glBindBuffer(GL_ARRAY_BUFFER, buf)
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, None)
- glDrawArrays(GL_TRIANGLES, 0, 3)
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, count)
glDisableVertexAttribArray(0)
- def textured_triangle(self, shader_location, texture, triangle, uvs):
+ @staticmethod
+ def bind_texture(shader_location, texture):
glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D, texture.gl_texid)
glUniform1i(shader_location, 0)
+
+ def textured_triangle(self, triangle, uvs, count=3):
glEnableVertexAttribArray(1)
glBindBuffer(GL_ARRAY_BUFFER, uvs)
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, None)
- self.triangle(triangle)
+ self.triangle(triangle, count)
glDisableVertexAttribArray(1)
def setup_texture(self, width: int, height: int, data: np.ndarray):
diff --git a/txtgameengine/shaders/__init__.py b/txtgameengine/shaders/__init__.py
index 64b0cc8..989ba98 100644
--- a/txtgameengine/shaders/__init__.py
+++ b/txtgameengine/shaders/__init__.py
@@ -1,2 +1,2 @@
from .shader import Shader
-from .integrated import TextureShader, BasicShader
+from .integrated import TextureShader, BasicShader, FontShader
diff --git a/txtgameengine/shaders/integrated.py b/txtgameengine/shaders/integrated.py
index d9e5ae5..180225a 100644
--- a/txtgameengine/shaders/integrated.py
+++ b/txtgameengine/shaders/integrated.py
@@ -14,3 +14,9 @@ class TextureShader(Shader):
UNIFORMS = dict(textureSampler="textureSampler")
VERTEX_PATH = shader_base_path / 'texture/vertex.glsl'
FRAGMENT_PATH = shader_base_path / 'texture/fragment.glsl'
+
+
+class FontShader(Shader):
+ UNIFORMS = dict(textureSampler="textureSampler")
+ VERTEX_PATH = shader_base_path / 'font/vertex.glsl'
+ FRAGMENT_PATH = shader_base_path / 'font/fragment.glsl'