changeset 436:36b907544820

fix: text-alignment, a few smaller fixes
author Sam <sam@basx.dev>
date Sun, 11 Feb 2024 18:47:13 +0700
parents 065854cdba52
children e75c6da1d261
files semicongine/core/fonttypes.nim semicongine/engine.nim semicongine/material.nim semicongine/panel.nim semicongine/renderer.nim semicongine/resources/font.nim semicongine/text.nim tests/test_font.nim tests/test_panel.nim
diffstat 9 files changed, 89 insertions(+), 55 deletions(-) [+]
line wrap: on
line diff
--- a/semicongine/core/fonttypes.nim	Sat Feb 10 21:19:43 2024 +0700
+++ b/semicongine/core/fonttypes.nim	Sun Feb 11 18:47:13 2024 +0700
@@ -33,4 +33,7 @@
     maxHeight*: int
     kerning*: Table[(Rune, Rune), float32]
     fontscale*: float32
+    lineHeight*: float32
     lineAdvance*: float32
+    capHeight*: float32
+    xHeight*: float32
--- a/semicongine/engine.nim	Sat Feb 10 21:19:43 2024 +0700
+++ b/semicongine/engine.nim	Sun Feb 11 18:47:13 2024 +0700
@@ -109,8 +109,9 @@
 
   assert not engine.renderer.isSome
   var allShaders = @shaders
+  allShaders.add (EMPTY_MATERIAL, EMPTY_SHADER)
+  allShaders.add (PANEL_MATERIAL_TYPE, PANEL_SHADER)
   allShaders.add (TEXT_MATERIAL_TYPE, TEXT_SHADER)
-  allShaders.add (PANEL_MATERIAL_TYPE, PANEL_SHADER)
   engine.renderer = some(engine.device.initRenderer(shaders = allShaders, clearColor = clearColor, backFaceCulling = backFaceCulling))
 
 proc initRenderer*(engine: var Engine, clearColor = Vec4f([0.8'f32, 0.8'f32, 0.8'f32, 1'f32])) =
--- a/semicongine/material.nim	Sat Feb 10 21:19:43 2024 +0700
+++ b/semicongine/material.nim	Sun Feb 11 18:47:13 2024 +0700
@@ -2,8 +2,8 @@
 import std/strformat
 import std/strutils
 
-
 import ./core
+import ./vulkan/shader
 
 type
   MaterialType* = object
@@ -49,46 +49,6 @@
 proc clearDirtyAttributes*(material: var MaterialData) =
   material.dirtyAttributes.reset
 
-let EMPTY_MATERIAL* = MaterialType(
-  name: "empty material",
-  vertexAttributes: {"position": Vec3F32}.toTable,
-)
-let COLORED_MATERIAL* = MaterialType(
-  name: "single color material",
-  vertexAttributes: {"position": Vec3F32}.toTable,
-  attributes: {"color": Vec4F32}.toTable,
-)
-let VERTEX_COLORED_MATERIAL* = MaterialType(
-  name: "vertex color material",
-  vertexAttributes: {
-    "position": Vec3F32,
-    "color": Vec4F32,
-  }.toTable,
-)
-let SINGLE_COLOR_MATERIAL* = MaterialType(
-  name: "single color material",
-  vertexAttributes: {
-    "position": Vec3F32,
-  }.toTable,
-  attributes: {"color": Vec4F32}.toTable
-)
-let SINGLE_TEXTURE_MATERIAL* = MaterialType(
-  name: "single texture material",
-  vertexAttributes: {
-    "position": Vec3F32,
-    "uv": Vec2F32,
-  }.toTable,
-  attributes: {"baseTexture": TextureType}.toTable
-)
-let COLORED_SINGLE_TEXTURE_MATERIAL* = MaterialType(
-  name: "colored single texture material",
-  vertexAttributes: {
-    "position": Vec3F32,
-    "uv": Vec2F32,
-  }.toTable,
-  attributes: {"baseTexture": TextureType, "color": Vec4F32}.toTable
-)
-
 proc `$`*(materialType: MaterialType): string =
   var attributes: seq[string]
   for key, value in materialType.attributes.pairs:
@@ -128,3 +88,52 @@
     theName = &"material instance of '{theType}'"
   initMaterialData(theType = theType, name = theName, attributes = attributes.toTable)
 
+const
+  EMPTY_MATERIAL* = MaterialType(
+    name: "empty material",
+    vertexAttributes: {"position": Vec3F32}.toTable,
+  )
+  COLORED_MATERIAL* = MaterialType(
+    name: "single color material",
+    vertexAttributes: {"position": Vec3F32}.toTable,
+    attributes: {"color": Vec4F32}.toTable,
+  )
+  VERTEX_COLORED_MATERIAL* = MaterialType(
+    name: "vertex color material",
+    vertexAttributes: {
+      "position": Vec3F32,
+      "color": Vec4F32,
+    }.toTable,
+  )
+  SINGLE_COLOR_MATERIAL* = MaterialType(
+    name: "single color material",
+    vertexAttributes: {
+      "position": Vec3F32,
+    }.toTable,
+    attributes: {"color": Vec4F32}.toTable
+  )
+  SINGLE_TEXTURE_MATERIAL* = MaterialType(
+    name: "single texture material",
+    vertexAttributes: {
+      "position": Vec3F32,
+      "uv": Vec2F32,
+    }.toTable,
+    attributes: {"baseTexture": TextureType}.toTable
+  )
+  COLORED_SINGLE_TEXTURE_MATERIAL* = MaterialType(
+    name: "colored single texture material",
+    vertexAttributes: {
+      "position": Vec3F32,
+      "uv": Vec2F32,
+    }.toTable,
+    attributes: {"baseTexture": TextureType, "color": Vec4F32}.toTable
+  )
+  EMPTY_SHADER* = createShaderConfiguration(
+    inputs = [
+      attr[Mat4](TRANSFORM_ATTRIB, memoryPerformanceHint = PreferFastWrite, perInstance = true),
+      attr[Vec3f]("position", memoryPerformanceHint = PreferFastWrite),
+    ],
+    outputs = [attr[Vec4f]("color")],
+    vertexCode = &"gl_Position = vec4(position, 1.0) * {TRANSFORM_ATTRIB};",
+    fragmentCode = &"color = vec4(1, 0, 1, 1);"
+  )
--- a/semicongine/panel.nim	Sat Feb 10 21:19:43 2024 +0700
+++ b/semicongine/panel.nim	Sun Feb 11 18:47:13 2024 +0700
@@ -155,6 +155,12 @@
   if value != panel.color:
     panel.mesh.material["color", 0] = value
 
+proc transform*(panel: Panel): Mat4 =
+  panel.mesh.transform
+proc `transform=`*(panel: var Panel, value: Mat4) =
+  if value != panel.transform:
+    panel.mesh.transform = value
+
 proc size*(panel: Panel): Vec2f =
   panel.size
 proc `size=`*(panel: var Panel, value: Vec2f) =
--- a/semicongine/renderer.nim	Sat Feb 10 21:19:43 2024 +0700
+++ b/semicongine/renderer.nim	Sun Feb 11 18:47:13 2024 +0700
@@ -383,10 +383,10 @@
               assert foundValue, &"Uniform '{uniform.name}' not found in scene shaderGlobals or materials"
             assert (uniform.arrayCount == 0 and value.len == 1) or value.len <= uniform.arrayCount, &"Uniform '{uniform.name}' found has wrong length (shader declares {uniform.arrayCount} but shaderGlobals and materials provide {value.len})"
             if value.len <= uniform.arrayCount:
-              warn &"Uniform '{uniform.name}' found has short length (shader declares {uniform.arrayCount} but shaderGlobals and materials provide {value.len})"
+              debug &"Uniform '{uniform.name}' found has short length (shader declares {uniform.arrayCount} but shaderGlobals and materials provide {value.len})"
             assert value.size <= uniform.size, &"During uniform update: gathered value has size {value.size} but uniform expects size {uniform.size}"
             if value.size <= uniform.size:
-              warn &"During uniform update: gathered value has size {value.size} but uniform expects size {uniform.size}"
+              debug &"During uniform update: gathered value has size {value.size} but uniform expects size {uniform.size}"
             debug &"  update uniform {uniform.name} with value: {value}"
             # TODO: technically we would only need to update the uniform buffer of the current
             # frameInFlight (I think), but we don't track for which frame the shaderglobals are no longer dirty
--- a/semicongine/resources/font.nim	Sat Feb 10 21:19:43 2024 +0700
+++ b/semicongine/resources/font.nim	Sun Feb 11 18:47:13 2024 +0700
@@ -1,4 +1,5 @@
 import std/tables
+import std/strutils
 import std/strformat
 import std/streams
 import std/os
@@ -10,11 +11,11 @@
 import ../core/fonttypes
 import ../algorithms
 
-{.emit: "#define STBTT_STATIC" .}
-{.emit: "#define STB_TRUETYPE_IMPLEMENTATION" .}
-{.emit: "#include \"" & currentSourcePath.parentDir() & "/stb_truetype.h\"" .}
+{.emit: "#define STBTT_STATIC".}
+{.emit: "#define STB_TRUETYPE_IMPLEMENTATION".}
+{.emit: "#include \"" & currentSourcePath.parentDir() & "/stb_truetype.h\"".}
 
-type stbtt_fontinfo {.importc, incompleteStruct .} = object
+type stbtt_fontinfo {.importc, incompleteStruct.} = object
 
 proc stbtt_InitFont(info: ptr stbtt_fontinfo, data: ptr char, offset: cint): cint {.importc, nodecl.}
 proc stbtt_ScaleForPixelHeight(info: ptr stbtt_fontinfo, pixels: cfloat): cfloat {.importc, nodecl.}
@@ -42,6 +43,8 @@
 
   var ascent, descent, lineGap: cint
   stbtt_GetFontVMetrics(addr fontinfo, addr ascent, addr descent, addr lineGap)
+
+  result.lineHeight = float32(ascent - descent) * result.fontscale
   result.lineAdvance = float32(ascent - descent + lineGap) * result.fontscale
 
   # ensure all codepoints are available in the font
@@ -69,6 +72,11 @@
       )
     topOffsets[codePoint] = offY
 
+    if char(codePoint) in UppercaseLetters:
+      result.capHeight = float32(height)
+    if codePoint == Rune('x'):
+      result.xHeight = float32(height)
+
     if width > 0 and height > 0:
       var bitmap = newSeq[GrayPixel](width * height)
       for i in 0 ..< width * height:
@@ -104,9 +112,9 @@
     result.glyphs[codePoint] = GlyphInfo(
       dimension: newVec2f(float32(image.width), float32(image.height)),
       uvs: [
-        newVec2f((coord.x + 0.5 )     / w, (coord.y + ih - 0.5) / h),
-        newVec2f((coord.x + 0.5 )     / w, (coord.y + 0.5)      / h),
-        newVec2f((coord.x + iw - 0.5) / w, (coord.y + 0.5)      / h),
+        newVec2f((coord.x + 0.5) / w, (coord.y + ih - 0.5) / h),
+        newVec2f((coord.x + 0.5) / w, (coord.y + 0.5) / h),
+        newVec2f((coord.x + iw - 0.5) / w, (coord.y + 0.5) / h),
         newVec2f((coord.x + iw - 0.5) / w, (coord.y + ih - 0.5) / h),
       ],
       topOffset: float32(topOffsets[codePoint]),
--- a/semicongine/text.nim	Sat Feb 10 21:19:43 2024 +0700
+++ b/semicongine/text.nim	Sun Feb 11 18:47:13 2024 +0700
@@ -84,14 +84,14 @@
       if i < text.processedText.len - 1:
         width += text.font.kerning[(text.processedText[i], text.processedText[i + 1])]
   lineWidths.add width
-  var height = float32(lineWidths.len) * text.font.lineAdvance
+  var height = float32(lineWidths.len - 1) * text.font.lineAdvance + text.font.capHeight
   if lineWidths[^1] == 0 and lineWidths.len > 1:
     height -= 1
 
   let anchorY = (case text.verticalAlignment
     of Top: 0'f32
     of Center: height / 2
-    of Bottom: height) - text.font.lineAdvance
+    of Bottom: height) - text.font.capHeight
 
   var
     offsetX = 0'f32
--- a/tests/test_font.nim	Sat Feb 10 21:19:43 2024 +0700
+++ b/tests/test_font.nim	Sun Feb 11 18:47:13 2024 +0700
@@ -12,6 +12,7 @@
   # build scene
   var scene = Scene(name: "main")
   var font = loadFont("DejaVuSans.ttf", lineHeightPixels = 210'f32)
+  var origin = initPanel(size = newVec2f(0.01, 0.01))
   var main_text = font.initText("", maxLen = 255, color = newVec4f(1, 0.15, 0.15, 1), scale = 0.0005, maxWidth = 1.0)
   var help_text = font.initText("""Controls
 
@@ -23,6 +24,7 @@
   F4: Top
   F5: Center
   F6: Bottom""", scale = 0.0002, position = newVec2f(-0.9, -0.9), horizontalAlignment = Left, verticalAlignment = Top)
+  scene.add origin
   scene.add main_text
   scene.add help_text
   engine.loadScene(scene)
@@ -35,6 +37,7 @@
     if engine.windowWasResized():
       var winSize = engine.getWindow().size
       main_text.aspect_ratio = winSize[0] / winSize[1]
+      origin.aspect_ratio = winSize[0] / winSize[1]
       help_text.aspect_ratio = winSize[0] / winSize[1]
 
     # add character
@@ -68,6 +71,7 @@
     elif engine.keyWasPressed(F5): main_text.verticalAlignment = Center
     elif engine.keyWasPressed(F6): main_text.verticalAlignment = Bottom
 
+    origin.refresh()
     main_text.text = main_text.text & Rune('_')
     main_text.refresh()
     main_text.text = main_text.text[0 ..< ^1]
--- a/tests/test_panel.nim	Sat Feb 10 21:19:43 2024 +0700
+++ b/tests/test_panel.nim	Sun Feb 11 18:47:13 2024 +0700
@@ -29,7 +29,7 @@
     font = loadFont("DejaVuSans.ttf", lineHeightPixels = 210'f32)
     scene = Scene(name: "main")
     origin = initPanel(
-      size = newVec2f(0.02, 0.02),
+      size = newVec2f(0.005, 0.005),
       color = newVec4f(1, 1, 1, 1),
       texture = Texture(isGrayscale: false, colorImage: newImage[RGBAPixel](3, 3, [T, B, T, B, B, B, T, B, T]), sampler: NEAREST_SAMPLER),
     )
@@ -49,7 +49,10 @@
 Vertical alignment:
   F4: Top
   F5: Center
-  F6: Bottom""", scale = 0.0002, position = newVec2f(-0.9, -0.9), horizontalAlignment = Left, verticalAlignment = Top)
+  F6: Bottom
+Mouse:
+  Left click: Increase counter
+  Right click: Decrease counter""", scale = 0.0002, position = newVec2f(-0.9, -0.9), horizontalAlignment = Left, verticalAlignment = Top)
 
   counterText = font.initText($counter, maxLen = 99, scale = 0.0004)