Mercurial > games > semicongine
changeset 1404:80cfa19d1e2c
did: finally get text/glyph layouting correct again, 2/4 test adapted to new glyph API
author | sam <sam@basx.dev> |
---|---|
date | Thu, 19 Dec 2024 23:32:45 +0700 |
parents | 02d302c868d5 |
children | 46bac138ad6c |
files | semicongine/text.nim tests/test_text.nim |
diffstat | 2 files changed, 85 insertions(+), 137 deletions(-) [+] |
line wrap: on
line diff
--- a/semicongine/text.nim Wed Dec 18 23:39:54 2024 +0700 +++ b/semicongine/text.nim Thu Dec 19 23:32:45 2024 +0700 @@ -135,13 +135,14 @@ result += font.advance[font.fallbackCharacter] * scale if i < text.len - 1: result += font.kerning.getOrDefault((text[i], text[i + 1]), 0) * scale + return result * 0.5 / getAspectRatio() proc textDimension*(font: Font, text: seq[Rune], scale: float32): Vec2f = let nLines = text.countIt(it == Rune('\n')).float32 - let h = nLines * font.lineAdvance * scale + font.lineHeight * scale + let h = (nLines * font.lineAdvance * scale + font.lineHeight * scale) * 0.5 let w = max(splitLines(text).toSeq.mapIt(width(font, it, scale))) - return vec2(w, h * 0.5) + return vec2(w, h) proc add*( glyphs: var Glyphs, @@ -162,11 +163,14 @@ &"Set {text.len} but Glyphs-object only supports {glyphs.position.len}" let - s = scale * glyphs.baseScale - d = textDimension(glyphs.font, text, s) - baselineStart = vec2(0, (glyphs.font.ascent + glyphs.font.descent) * s) - pos = position.xy - anchor * d + baselineStart - lineWidths = splitLines(text).toSeq.mapIt(width(glyphs.font, it, s)) + globalScale = scale * glyphs.baseScale + dim = textDimension(glyphs.font, text, globalScale) + baselineStart = vec2(0, glyphs.font.ascent * globalScale * 0.5) + pos = position.xy - anchor * dim + baselineStart + # lineWidths need to be converted to NDC + lineWidths = splitLines(text).toSeq.mapIt(width(glyphs.font, it, globalScale)) + # also dimension must be in NDC + maxWidth = dim.x var origin = vec3( @@ -179,9 +183,9 @@ of Left: cursorPos.x = origin.x of Center: - cursorPos.x = origin.x - ((lineWidths[lineI] - d.x) / 2) + cursorPos.x = origin.x + ((maxWidth - lineWidths[lineI]) / 2) of Right: - cursorPos.x = origin.x - (lineWidths[lineI] - d.x) + cursorPos.x = origin.x + (maxWidth - lineWidths[lineI]) * getAspectRatio() * 2 for i in 0 ..< text.len: if text[i] == Rune('\n'): @@ -190,14 +194,14 @@ of Left: cursorPos.x = origin.x of Center: - cursorPos.x = origin.x - ((lineWidths[lineI] - d.x) / 2) + cursorPos.x = origin.x + ((maxWidth - lineWidths[lineI]) / 2) of Right: - cursorPos.x = origin.x - (lineWidths[lineI] - d.x) - cursorPos.y = cursorPos.y - glyphs.font.lineAdvance * s + cursorPos.x = origin.x + (maxWidth - lineWidths[lineI]) * getAspectRatio() * 2 + cursorPos.y = cursorPos.y - glyphs.font.lineAdvance * globalScale else: if not text[i].isWhitespace(): glyphs.position[glyphs.cursor] = cursorPos - glyphs.scale[glyphs.cursor] = s + glyphs.scale[glyphs.cursor] = globalScale glyphs.color[glyphs.cursor] = color if text[i] in glyphs.font.descriptorGlyphIndex: glyphs.glyphIndex[glyphs.cursor] = glyphs.font.descriptorGlyphIndex[text[i]] @@ -207,14 +211,15 @@ inc glyphs.cursor if text[i] in glyphs.font.advance: - cursorPos.x = cursorPos.x + glyphs.font.advance[text[i]] * s + cursorPos.x = cursorPos.x + glyphs.font.advance[text[i]] * globalScale else: cursorPos.x = - cursorPos.x + glyphs.font.advance[glyphs.font.fallbackCharacter] * s + cursorPos.x + glyphs.font.advance[glyphs.font.fallbackCharacter] * globalScale if i < text.len - 1: cursorPos.x = - cursorPos.x + glyphs.font.kerning.getOrDefault((text[i], text[i + 1]), 0) * s + cursorPos.x + + glyphs.font.kerning.getOrDefault((text[i], text[i + 1]), 0) * globalScale proc add*( glyphs: var Glyphs,
--- a/tests/test_text.nim Wed Dec 18 23:39:54 2024 +0700 +++ b/tests/test_text.nim Thu Dec 19 23:32:45 2024 +0700 @@ -14,15 +14,16 @@ type FontDS = object fontAtlas: Image[Gray] -type EMPTY = object +const MAX_CODEPOINTS = 200 +const FONTNAME = "Overhaul.ttf" +# const FONTNAME = "DejaVuSans.ttf" -const MAX_GLYPHS = 200 -proc test_01_static_label_new(time: float32) = - # var font = loadFont[MAX_GLYPHS]("Overhaul.ttf", lineHeightPixels = 200) - var font = loadFont[MAX_GLYPHS]("DejaVuSans.ttf", lineHeightPixels = 200) +proc test_01_static_label(time: float32) = + var font = loadFont[MAX_CODEPOINTS](FONTNAME, lineHeightPixels = 200) var renderdata = initRenderData() - var pipeline = - createPipeline[GlyphShader[MAX_GLYPHS]](renderPass = vulkan.swapchain.renderPass) + var pipeline = createPipeline[GlyphShader[MAX_CODEPOINTS]]( + renderPass = vulkan.swapchain.renderPass + ) var glyphs = font.initGlyphs(1000, baseScale = 0.1) assignBuffers(renderdata, glyphs) @@ -34,27 +35,7 @@ while ((getMonoTime() - start).inMilliseconds().int / 1000) < time: let t = getMonoTime() glyphs.reset() - glyphs.add("+", vec3(0.0, 0.0), anchor = vec2(0.5, 0.5), color = vec4(0, 0, 0, 1)) - glyphs.add( - "Hello world\nHow are you today?\nWell, I am fine".toRunes(), - vec3(0.5, 0.5), - alignment = Right, - anchor = vec2(1, 0.5), - color = vec4(0, 0, 0, 1), - ) - glyphs.add( - "Hello world\nHow are you today?\nWell, I am fine".toRunes(), - vec3(0.5, 0.5), - alignment = Left, - anchor = vec2(0, 0.5), - color = vec4(0, 0, 0, 1), - ) - glyphs.add("semi-\ncon-\ngine".toRunes(), vec3(0.5, -0.5), color = vec4(0, 0, 0, 1)) - glyphs.add("semi-\ncon-\ngine".toRunes(), vec3(-0.5, 0.5), color = vec4(0, 0, 0, 1)) - # glyphs.add("11111111111111111", vec3(0.5, 0.5), vec2(1, 0)) - # glyphs.add("22222222222222222", vec3(0.5, 0.5)) - # glyphs.add("33333333333333333", vec3(0.5, 0.5), vec2(0, 1)) - glyphs.add("semi-\ncon-\ngineb".toRunes(), vec3(0.1, 0.9), color = vec4(0, 0, 0, 1)) + glyphs.add("Hello semicongine!", vec3(0.5, 0.5), anchor = vec2(0.5, 0.5)) glyphs.updateAllGPUBuffers(flush = true) withNextFrame(framebuffer, commandbuffer): @@ -65,7 +46,7 @@ commandbuffer, vulkan.swapchain.width, vulkan.swapchain.height, - vec4(1, 1, 1, 1), + vec4(0, 0, 0, 0), ): withPipeline(commandbuffer, pipeline): renderGlyphs(commandbuffer, pipeline, glyphs) @@ -75,74 +56,52 @@ destroyPipeline(pipeline) destroyRenderData(renderdata) -#[ -proc test_01_static_label(time: float32) = - var font = loadFont[MAX_GLYPHS]("Overhaul.ttf", lineHeightPixels = 160) - var renderdata = initRenderData() - var pipeline = - createPipeline[GlyphShader[MAX_GLYPHS]](renderPass = vulkan.swapchain.renderPass) - - var ds = asDescriptorSetData(FontDS(fontAtlas: font.fontAtlas.copy())) - uploadImages(renderdata, ds) - initDescriptorSet(renderdata, pipeline.layout(0), ds) - - var label1 = - initTextbox(renderdata, pipeline.layout(0), font, 0.0005, "Hello semicongine!") - - var start = getMonoTime() - while ((getMonoTime() - start).inMilliseconds().int / 1000) < time: - label1.refresh() - withNextFrame(framebuffer, commandbuffer): - bindDescriptorSet(commandbuffer, ds, 0, pipeline) - withRenderPass( - vulkan.swapchain.renderPass, - framebuffer, - commandbuffer, - vulkan.swapchain.width, - vulkan.swapchain.height, - vec4(0, 0, 0, 0), - ): - withPipeline(commandbuffer, pipeline): - render(commandbuffer, pipeline, label1, vec3(), vec4(1, 1, 1, 1)) - # cleanup - checkVkResult vkDeviceWaitIdle(vulkan.device) - destroyPipeline(pipeline) - destroyRenderData(renderdata) - -proc test_02_multiple_animated(time: float32) = - var font1 = loadFont[MAX_GLYPHS]("Overhaul.ttf", lineHeightPixels = 40) - var font2 = loadFont[MAX_GLYPHS]("Overhaul.ttf", lineHeightPixels = 160) - var font3 = loadFont[MAX_GLYPHS]("DejaVuSans.ttf", lineHeightPixels = 160) +proc test_02_multi_counter(time: float32) = + var font1 = loadFont[MAX_CODEPOINTS]("Overhaul.ttf", lineHeightPixels = 40) + var font2 = loadFont[MAX_CODEPOINTS]("Overhaul.ttf", lineHeightPixels = 160) + var font3 = loadFont[MAX_CODEPOINTS]("DejaVuSans.ttf", lineHeightPixels = 160) var renderdata = initRenderData() - var pipeline = - createPipeline[GlyphShader[MAX_GLYPHS]](renderPass = vulkan.swapchain.renderPass) - - var ds1 = asDescriptorSetData(FontDS(fontAtlas: font1.fontAtlas.copy())) - uploadImages(renderdata, ds1) - initDescriptorSet(renderdata, pipeline.layout(0), ds1) + var pipeline = createPipeline[GlyphShader[MAX_CODEPOINTS]]( + renderPass = vulkan.swapchain.renderPass + ) - var ds2 = asDescriptorSetData(FontDS(fontAtlas: font2.fontAtlas.copy())) - uploadImages(renderdata, ds2) - initDescriptorSet(renderdata, pipeline.layout(0), ds2) + assignBuffers(renderdata, font1.descriptorSet) + assignBuffers(renderdata, font2.descriptorSet) + assignBuffers(renderdata, font3.descriptorSet) + uploadImages(renderdata, font1.descriptorSet) + uploadImages(renderdata, font2.descriptorSet) + uploadImages(renderdata, font3.descriptorSet) + initDescriptorSet(renderdata, pipeline.layout(0), font1.descriptorSet) + initDescriptorSet(renderdata, pipeline.layout(0), font2.descriptorSet) + initDescriptorSet(renderdata, pipeline.layout(0), font3.descriptorSet) - var ds3 = asDescriptorSetData(FontDS(fontAtlas: font3.fontAtlas.copy())) - uploadImages(renderdata, ds3) - initDescriptorSet(renderdata, pipeline.layout(0), ds3) + var glyphs1 = font1.initGlyphs(10, baseScale = 0.1) + var glyphs2 = font2.initGlyphs(10, baseScale = 0.1) + var glyphs3 = font3.initGlyphs(10, baseScale = 0.1) - var labels = [ - initTextbox(renderdata, pipeline.layout(0), font1, 0.004, " 0"), - initTextbox(renderdata, pipeline.layout(0), font2, 0.001, " 1"), - initTextbox(renderdata, pipeline.layout(0), font3, 0.001, " 2"), - ] + assignBuffers(renderdata, glyphs1) + assignBuffers(renderdata, glyphs2) + assignBuffers(renderdata, glyphs3) + + var labels = [" 0", " 1", " 2"] var start = getMonoTime() var p = 0 while ((getMonoTime() - start).inMilliseconds().int / 1000) < time: let progress = ((getMonoTime() - start).inMilliseconds().int / 1000) / time - for i in 0 ..< labels.len: - labels[i].text = $(p + i) - labels[i].refresh() + glyphs1.reset() + glyphs2.reset() + glyphs3.reset() + + glyphs1.add($(p + 0), vec3(0.3, 0.5)) + glyphs2.add($(p + 1), vec3(0.5, 0.5)) + glyphs3.add($(p + 2), vec3(0.7, 0.5)) + + glyphs1.updateAllGPUBuffers(flush = true) + glyphs2.updateAllGPUBuffers(flush = true) + glyphs3.updateAllGPUBuffers(flush = true) + inc p withNextFrame(framebuffer, commandbuffer): withRenderPass( @@ -154,42 +113,26 @@ vec4(0, 0, 0, 0), ): withPipeline(commandbuffer, pipeline): - bindDescriptorSet(commandbuffer, ds1, 0, pipeline) - render( - commandbuffer, - pipeline, - labels[0], - position = vec3(0 / labels.len, 0.1 + progress * 0.5), - color = vec4(1, 1, 1, 1), - ) - bindDescriptorSet(commandbuffer, ds2, 0, pipeline) - render( - commandbuffer, - pipeline, - labels[1], - position = vec3(1 / labels.len, 0.1 + progress * 0.5), - color = vec4(1, 1, 1, 1), - ) - bindDescriptorSet(commandbuffer, ds3, 0, pipeline) - render( - commandbuffer, - pipeline, - labels[2], - position = vec3(2 / labels.len, 0.1 + progress * 0.5), - color = vec4(1, 1, 1, 1), - ) + bindDescriptorSet(commandbuffer, font1.descriptorSet, 0, pipeline) + renderGlyphs(commandbuffer, pipeline, glyphs1) + bindDescriptorSet(commandbuffer, font2.descriptorSet, 0, pipeline) + renderGlyphs(commandbuffer, pipeline, glyphs2) + bindDescriptorSet(commandbuffer, font3.descriptorSet, 0, pipeline) + renderGlyphs(commandbuffer, pipeline, glyphs3) # cleanup checkVkResult vkDeviceWaitIdle(vulkan.device) destroyPipeline(pipeline) destroyRenderData(renderdata) +#[ proc test_03_layouting(time: float32) = - var font = loadFont[MAX_GLYPHS]("DejaVuSans.ttf", lineHeightPixels = 40) + var font = loadFont[MAX_CODEPOINTS]("DejaVuSans.ttf", lineHeightPixels = 40) var renderdata = initRenderData() - var pipeline = - createPipeline[GlyphShader[MAX_GLYPHS]](renderPass = vulkan.swapchain.renderPass) + var pipeline = createPipeline[GlyphShader[MAX_CODEPOINTS]]( + renderPass = vulkan.swapchain.renderPass + ) var ds = asDescriptorSetData(FontDS(fontAtlas: font.fontAtlas.copy())) uploadImages(renderdata, ds) @@ -258,11 +201,12 @@ destroyRenderData(renderdata) proc test_04_lots_of_texts(time: float32) = - var font = loadFont[MAX_GLYPHS]("DejaVuSans.ttf", lineHeightPixels = 160) + var font = loadFont[MAX_CODEPOINTS]("DejaVuSans.ttf", lineHeightPixels = 160) var renderdata = initRenderData() - var pipeline = - createPipeline[GlyphShader[MAX_GLYPHS]](renderPass = vulkan.swapchain.renderPass) + var pipeline = createPipeline[GlyphShader[MAX_CODEPOINTS]]( + renderPass = vulkan.swapchain.renderPass + ) var ds = asDescriptorSetData(FontDS(fontAtlas: font.fontAtlas.copy())) uploadImages(renderdata, ds) @@ -306,7 +250,7 @@ ]# when isMainModule: - var time = 1000'f32 + var time = 1'f32 initVulkan() for depthBuffer in [true, false]: @@ -314,9 +258,8 @@ setupSwapchain(renderpass = renderpass) # tests a simple triangle with minimalistic shader and vertex format - test_01_static_label_new(time) - # test_01_static_label(time) - # test_02_multiple_animated(time) + test_01_static_label(time) + test_02_multi_counter(time) # test_03_layouting(time) # test_04_lots_of_texts(time)