Skip to content

Commit cd1968e

Browse files
committed
Working initial text writer
1 parent 9e02d88 commit cd1968e

File tree

9 files changed

+135
-35
lines changed

9 files changed

+135
-35
lines changed

demosys/text/__init__.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import os
22
from demosys.conf import settings
33

4-
from .writer_2d import TextWriter2D # noqa
5-
from .renderer_2d import TextRenderer2D # noqa
4+
from .writer2d import TextWriter2D # noqa
5+
from .renderer2d import TextRenderer2D # noqa
66

7-
8-
settings.add_shader_dir(os.path.join(os.path.dirname(__file__), 'shaders'))
7+
# Register resource paths
8+
settings.add_shader_dir(os.path.join(os.path.dirname(__file__), 'resources', 'shaders'))
9+
settings.add_texture_dir(os.path.join(os.path.dirname(__file__), 'resources', 'textures'))
10+
settings.add_data_dir(os.path.join(os.path.dirname(__file__), 'resources', 'data'))

demosys/text/base.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
"""
22
Text writer using monospace font from texture
33
"""
4+
import json
5+
46
from demosys import context
57

68

@@ -11,6 +13,41 @@ class BaseText:
1113

1214
def __init__(self):
1315
self.ctx = context.ctx()
16+
self._meta = None
17+
self._ct = None
1418

1519
def draw(self, *args, **kwargs):
1620
raise NotImplementedError()
21+
22+
def _translate_data(self, data):
23+
"""Translate character bytes into texture positions"""
24+
return [self._meta.characters - 1 - self._ct[c] for c in data]
25+
26+
def _init(self, meta: 'Meta'):
27+
self._meta = meta
28+
# Check if the atlas size is sane
29+
if not self._meta.characters * self._meta.character_height == self._meta.atlas_height:
30+
raise ValueError("characters * character_width != atlas_height")
31+
32+
self._generate_character_map()
33+
34+
def _generate_character_map(self):
35+
"""Generate character translation map (latin1 pos to texture pos)"""
36+
self._ct = [-1] * 256
37+
index = 0
38+
for crange in self._meta.character_ranges:
39+
for cpos in range(crange['min'], crange['max'] + 1):
40+
self._ct[cpos] = index
41+
index += 1
42+
43+
44+
class Meta:
45+
"""Font metadata"""
46+
def __init__(self, meta):
47+
self._meta = json.loads(meta)
48+
self.characters = self._meta['characters']
49+
self.character_ranges = self._meta['character_ranges']
50+
self.character_height = self._meta['character_height']
51+
self.character_width = self._meta['character_width']
52+
self.atlas_height = self._meta['atlas_height']
53+
self.atlas_width = self._meta['atlas_width']
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from demosys.resources import shaders
12
from .base import BaseText
23

34

@@ -11,11 +12,12 @@ def __init__(self, area, size=0.1, text=""):
1112
"""
1213
super().__init__(text)
1314
self.area = area
15+
self.size = size
1416
self._text = text
1517

1618
self.fbo = None
1719
self.vao = None
18-
self.shader = None
20+
self.shader = shaders.get('demosys/text/textwriter.glsl', create=True)
1921

2022
@property
2123
def text(self):
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"characters": 190,
3+
"character_ranges": [
4+
{
5+
"min": 32,
6+
"max": 126
7+
},
8+
{
9+
"min": 161,
10+
"max": 255
11+
}
12+
],
13+
"character_height": 159,
14+
"character_width": 77,
15+
"atlas_height": 30210,
16+
"atlas_width": 77
17+
}

demosys/text/shaders/demosys/text/textwriter.glsl renamed to demosys/text/resources/shaders/demosys/text/textwriter.glsl

File renamed without changes.
311 KB
Loading

demosys/text/writer2d.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import numpy
2+
import moderngl
3+
from demosys.opengl import VAO
4+
from demosys.resources import data, shaders, textures
5+
from demosys.opengl import TextureArray
6+
7+
from .base import BaseText, Meta
8+
9+
10+
class TextWriter2D(BaseText):
11+
12+
def __init__(self, area, size=0.1, text=""):
13+
"""
14+
:param area: (x, y) Text area size (number of characters)
15+
:param size: Text size
16+
:param text: Initial text
17+
"""
18+
super().__init__()
19+
self.area = area
20+
self.size = size
21+
self._text = text.encode('latin1')
22+
23+
self._vao = None
24+
self._texture = textures.get('demosys/text/VeraMono.png', cls=TextureArray, layers=190, create=True)
25+
self._shader = shaders.get('demosys/text/textwriter.glsl', create=True)
26+
self._config = data.get('demosys/text/meta.json', create=True)
27+
28+
data.on_loaded(self._post_load)
29+
30+
def _post_load(self):
31+
"""Parse font metadata after resources are loaded"""
32+
self._init(Meta(self._config.data))
33+
34+
self._string_data = self._translate_data(self._text)
35+
self._string_buffer = self.ctx.buffer(
36+
data=numpy.array(self._string_data, dtype=numpy.uint32).tobytes()
37+
)
38+
self._pos = self.ctx.buffer(data=bytes([0] * 4 * 3))
39+
40+
self._vao = VAO("textwriter", mode=moderngl.POINTS)
41+
self._vao.buffer(self._pos, '3f', 'in_position')
42+
self._vao.buffer(self._string_buffer, '1u', 'in_char_id', per_instance=True)
43+
44+
@property
45+
def text(self):
46+
return self._text
47+
48+
@text.setter
49+
def text(self, value):
50+
self._text = value
51+
52+
def draw(self, proj_matrix, view_matrix):
53+
# print(self._text, self._string_data)
54+
self._texture.use(location=0)
55+
self._shader.uniform("m_proj", proj_matrix.astype('f4').tobytes())
56+
self._shader.uniform("m_mv", view_matrix.astype('f4').tobytes())
57+
self._shader.uniform("font_texture", 0)
58+
self._shader.uniform("char_size", (self._meta.character_width / self._meta.character_height, 1.0))
59+
self._vao.draw(self._shader, instances=len(self._string_data))

demosys/text/writer_2d.py

Lines changed: 0 additions & 28 deletions
This file was deleted.

examples/text/effect.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,23 @@
1+
import moderngl
12
from demosys.effects import effect
23
from demosys.text import TextWriter2D
4+
from pyrr import matrix44
35

46

57
class TextEffect(effect.Effect):
68

79
def __init__(self):
8-
self.writer = TextWriter2D((10, 1), text="Hello world!")
10+
super().__init__()
11+
self.writer = TextWriter2D((10, 1),
12+
text="Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world!")
13+
14+
def post_load(self):
15+
pass
916

1017
@effect.bind_target
1118
def draw(self, time, frametime, target):
12-
self.writer.draw()
19+
self.ctx.disable(moderngl.CULL_FACE)
20+
m_proj = self.create_projection()
21+
# m_mv = matrix44.create_identity()
22+
23+
self.writer.draw(m_proj, self.sys_camera.view_matrix)

0 commit comments

Comments
 (0)