changeset 1406:aeb15aa9768c

did: continue text layout, improve vector api
author sam <sam@basx.dev>
date Sat, 21 Dec 2024 19:32:59 +0700
parents 46bac138ad6c
children 56f927b89716
files semicongine/core/vector.nim semicongine/text.nim tests/test_text.nim
diffstat 3 files changed, 62 insertions(+), 63 deletions(-) [+]
line wrap: on
line diff
--- a/semicongine/core/vector.nim	Sat Dec 21 00:19:11 2024 +0700
+++ b/semicongine/core/vector.nim	Sat Dec 21 19:32:59 2024 +0700
@@ -450,30 +450,30 @@
     TVec4[T]([vec[0] / l, vec[1] / l, vec[2] / l, vec[3] / l])
 
 # scalar operations
-func `+`*(a: TVec1, b: SomeNumber): auto =
-  TVec1([a[0] + b])
-func `+`*(a: TVec2, b: SomeNumber): auto =
-  TVec2([a[0] + b, a[1] + b])
-func `+`*(a: TVec3, b: SomeNumber): auto =
-  TVec3([a[0] + b, a[1] + b, a[2] + b])
-func `+`*(a: TVec4, b: SomeNumber): auto =
-  TVec4([a[0] + b, a[1] + b, a[2] + b, a[3] + b])
-func `-`*(a: TVec1, b: SomeNumber): auto =
-  TVec1([a[0] - b])
-func `-`*(a: TVec2, b: SomeNumber): auto =
-  TVec2([a[0] - b, a[1] - b])
-func `-`*(a: TVec3, b: SomeNumber): auto =
-  TVec3([a[0] - b, a[1] - b, a[2] - b])
-func `-`*(a: TVec4, b: SomeNumber): auto =
-  TVec4([a[0] - b, a[1] - b, a[2] - b, a[3] - b])
-func `*`*(a: TVec1, b: SomeNumber): auto =
-  TVec1([a[0] * b])
-func `*`*(a: TVec2, b: SomeNumber): auto =
-  TVec2([a[0] * b, a[1] * b])
-func `*`*(a: TVec3, b: SomeNumber): auto =
-  TVec3([a[0] * b, a[1] * b, a[2] * b])
-func `*`*(a: TVec4, b: SomeNumber): auto =
-  TVec4([a[0] * b, a[1] * b, a[2] * b, a[3] * b])
+func `+`*[T](a: TVec1[T], b: SomeNumber): auto =
+  TVec1([a[0] + T(b)])
+func `+`*[T](a: TVec2[T], b: SomeNumber): auto =
+  TVec2([a[0] + T(b), a[1] + T(b)])
+func `+`*[T](a: TVec3[T], b: SomeNumber): auto =
+  TVec3([a[0] + T(b), a[1] + T(b), a[2] + T(b)])
+func `+`*[T](a: TVec4[T], b: SomeNumber): auto =
+  TVec4([a[0] + T(b), a[1] + T(b), a[2] + T(b), a[3] + T(b)])
+func `-`*[T](a: TVec1[T], b: SomeNumber): auto =
+  TVec1([a[0] - T(b)])
+func `-`*[T](a: TVec2[T], b: SomeNumber): auto =
+  TVec2([a[0] - T(b), a[1] - T(b)])
+func `-`*[T](a: TVec3[T], b: SomeNumber): auto =
+  TVec3([a[0] - T(b), a[1] - T(b), a[2] - T(b)])
+func `-`*[T](a: TVec4[T], b: SomeNumber): auto =
+  TVec4([a[0] - T(b), a[1] - T(b), a[2] - T(b), a[3] - T(b)])
+func `*`*[T](a: TVec1[T], b: SomeNumber): auto =
+  TVec1([a[0] * T(b)])
+func `*`*[T](a: TVec2[T], b: SomeNumber): auto =
+  TVec2([a[0] * T(b), a[1] * T(b)])
+func `*`*[T](a: TVec3[T], b: SomeNumber): auto =
+  TVec3([a[0] * T(b), a[1] * T(b), a[2] * T(b)])
+func `*`*[T](a: TVec4[T], b: SomeNumber): auto =
+  TVec4([a[0] * T(b), a[1] * T(b), a[2] * T(b), a[3] * T(b)])
 func `div`*[T, S: SomeInteger](a: TVec1[T], b: S): auto =
   TVec1[T]([a[0] div T(b)])
 func `/`*[T, S: SomeFloat](a: TVec1[T], b: S): auto =
--- a/semicongine/text.nim	Sat Dec 21 00:19:11 2024 +0700
+++ b/semicongine/text.nim	Sat Dec 21 19:32:59 2024 +0700
@@ -79,17 +79,12 @@
 
 void main() {
   int vertexI = indices[gl_VertexIndex];
-  vec3 pos = vec3(
+  vec3 vertexPos = vec3(
     glyphquads.pos[glyphIndex][i_x[vertexI]] * scale / textRendering.aspectRatio,
     glyphquads.pos[glyphIndex][i_y[vertexI]] * scale,
     1 - (gl_InstanceIndex + 1) * epsilon // allows overlapping glyphs to make proper depth test
   );
-  vec3 offset = vec3(
-    (position.x - textRendering.aspectRatio + 1) / textRendering.aspectRatio,
-    position.y,
-    position.z
-  );
-  gl_Position = vec4(pos + offset, 1.0);
+  gl_Position = vec4(vertexPos + position, 1.0);
   vec2 uv = vec2(glyphquads.uv[glyphIndex][i_x[vertexI]], glyphquads.uv[glyphIndex][i_y[vertexI]]);
   fragmentUv = uv;
   fragmentColor = color;
@@ -135,11 +130,11 @@
         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()
+  return result
 
 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) * 0.5
+  let h = (nLines * font.lineAdvance * scale + font.lineHeight * scale)
   let w = max(splitLines(text).toSeq.mapIt(width(font, it, scale)))
 
   return vec2(w, h)
@@ -149,7 +144,7 @@
     text: seq[Rune],
     position: Vec3f,
     alignment: TextAlignment = Left,
-    anchor: Vec2f = vec2(0, 1),
+    anchor: Vec2f = vec2(-1, 1),
     scale: float32 = 1'f32,
     color: Vec4f = vec4(1, 1, 1, 1),
 ) =
@@ -165,17 +160,16 @@
   let
     globalScale = scale * glyphs.baseScale
     dim = textDimension(glyphs.font, text, globalScale)
-    baselineStart = vec2(0, glyphs.font.ascent * globalScale * 0.5)
+    baselineStart = vec2(0, glyphs.font.ascent * globalScale)
     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
+    aratio = getAspectRatio()
 
   var
-    origin = vec3(
-      pos.x * getAspectRatio() * 2'f32 - 1'f32, -(pos.y * 2'f32 - 1'f32), position.z
-    )
+    origin = vec3(pos.x, pos.y, position.z)
     cursorPos = origin
     lineI = 0
 
@@ -185,7 +179,7 @@
   of Center:
     cursorPos.x = origin.x + ((maxWidth - lineWidths[lineI]) / 2)
   of Right:
-    cursorPos.x = origin.x + (maxWidth - lineWidths[lineI]) * getAspectRatio() * 2
+    cursorPos.x = origin.x + (maxWidth - lineWidths[lineI]) * aratio * 2
 
   for i in 0 ..< text.len:
     if text[i] == Rune('\n'):
@@ -196,7 +190,7 @@
       of Center:
         cursorPos.x = origin.x + ((maxWidth - lineWidths[lineI]) / 2)
       of Right:
-        cursorPos.x = origin.x + (maxWidth - lineWidths[lineI]) * getAspectRatio() * 2
+        cursorPos.x = origin.x + (maxWidth - lineWidths[lineI]) * aratio * 2
       cursorPos.y = cursorPos.y - glyphs.font.lineAdvance * globalScale
     else:
       if not text[i].isWhitespace():
@@ -211,15 +205,17 @@
         inc glyphs.cursor
 
       if text[i] in glyphs.font.advance:
-        cursorPos.x = cursorPos.x + glyphs.font.advance[text[i]] * globalScale
+        cursorPos.x = cursorPos.x + glyphs.font.advance[text[i]] * globalScale / aratio
       else:
         cursorPos.x =
-          cursorPos.x + glyphs.font.advance[glyphs.font.fallbackCharacter] * globalScale
+          cursorPos.x +
+          glyphs.font.advance[glyphs.font.fallbackCharacter] * globalScale / aratio
 
       if i < text.len - 1:
         cursorPos.x =
           cursorPos.x +
-          glyphs.font.kerning.getOrDefault((text[i], text[i + 1]), 0) * globalScale
+          glyphs.font.kerning.getOrDefault((text[i], text[i + 1]), 0) * globalScale /
+          aratio
 
 proc add*(
     glyphs: var Glyphs,
--- a/tests/test_text.nim	Sat Dec 21 00:19:11 2024 +0700
+++ b/tests/test_text.nim	Sat Dec 21 19:32:59 2024 +0700
@@ -123,15 +123,13 @@
   destroyRenderData(renderdata)
 
 proc test_03_layouting(time: float32) =
-  var font = loadFont[MAX_CODEPOINTS]("DejaVuSans.ttf", lineHeightPixels = 40)
+  var font = loadFont[MAX_CODEPOINTS]("DejaVuSans.ttf", lineHeightPixels = 160)
   var renderdata = initRenderData()
 
   var pipeline = createPipeline[GlyphShader[MAX_CODEPOINTS]](
     renderPass = vulkan.swapchain.renderPass
   )
 
-  var ds = asDescriptorSetData(FontDS(fontAtlas: font.fontAtlas.copy()))
-
   assignBuffers(renderdata, font.descriptorSet)
   uploadImages(renderdata, font.descriptorSet)
   initDescriptorSet(renderdata, pipeline.layout(0), font.descriptorSet)
@@ -139,23 +137,28 @@
   var glyphs = font.initGlyphs(1000, baseScale = 0.1)
   assignBuffers(renderdata, glyphs)
 
-  for horizontal in HorizontalAlignment:
-    glyphs.add $horizontal & " aligned"
-  for vertical in VerticalAlignment:
-    glyphs.add $vertical & " aligned"
-
-  glyphs.add(
-    """Paragraph
-This is a somewhat longer paragraph with a few newlines and a maximum width of 0.2.
-
-It should display with some space above and have a pleasing appearance overall! :)""",
-    verticalAlignment = Top,
-    horizontalAlignment = Left,
-  )
-
   var start = getMonoTime()
   while ((getMonoTime() - start).inMilliseconds().int / 1000) < time:
     let progress = ((getMonoTime() - start).inMilliseconds().int / 1000) / time
+
+    glyphs.reset()
+    glyphs.add("Anchor Center", vec3(0, 0), anchor = vec2(0, 0))
+    glyphs.add("Anchor top left", vec3(0, 0), anchor = vec2(-1, 1))
+    glyphs.add("Anchor top right", vec3(0, 0), anchor = vec2(1, 1))
+    glyphs.add("Anchor bottom left", vec3(0, 0), anchor = vec2(-1, -1))
+    glyphs.add("Anchor bottom right", vec3(0, 0), anchor = vec2(1, -1))
+
+    glyphs.add(
+      """Paragraph
+  This is a somewhat longer paragraph with a few newlines and a maximum width of 0.2.
+
+  It should display with some space above and have a pleasing appearance overall! :)""",
+      vec3(0.5, 0.5),
+      anchor = vec2(0, 0),
+      alignment = Center,
+    )
+    glyphs.updateAllGPUBuffers(flush = true)
+
     withNextFrame(framebuffer, commandbuffer):
       bindDescriptorSet(commandbuffer, font.descriptorSet, 0, pipeline)
       withRenderPass(
@@ -225,7 +228,7 @@
 ]#
 
 when isMainModule:
-  var time = 1'f32
+  var time = 100'f32
   initVulkan()
 
   for depthBuffer in [true, false]:
@@ -233,8 +236,8 @@
     setupSwapchain(renderpass = renderpass)
 
     # tests a simple triangle with minimalistic shader and vertex format
-    test_01_static_label(time)
-    test_02_multi_counter(time)
+    # test_01_static_label(time)
+    # test_02_multi_counter(time)
     test_03_layouting(time)
     # test_04_lots_of_texts(time)