Use per-char width instead of fixed advance in font atlas

This gives us more (much?) efficient packing of letters
and we load exact size of the letters.

It looks visually a lot better too!
This wasn't good with normal smapling, but using texelFetch its
great.
This commit is contained in:
bloeys
2022-07-07 15:33:51 +04:00
parent d23e833b54
commit 16bfe7f05b
5 changed files with 195 additions and 99 deletions

View File

@ -2,6 +2,7 @@ package glyphs
import ( import (
"errors" "errors"
"fmt"
"image" "image"
"image/color" "image/color"
"image/draw" "image/draw"
@ -10,7 +11,6 @@ import (
"os" "os"
"unicode" "unicode"
"github.com/bloeys/gglm/gglm"
"github.com/bloeys/nterm/assert" "github.com/bloeys/nterm/assert"
"github.com/golang/freetype/truetype" "github.com/golang/freetype/truetype"
"golang.org/x/image/font" "golang.org/x/image/font"
@ -25,12 +25,13 @@ type FontAtlas struct {
//Advance is global to the atlas because we only support monospaced fonts //Advance is global to the atlas because we only support monospaced fonts
Advance int Advance int
LineHeight int LineHeight int
SizeUV gglm.Vec2
} }
type FontAtlasGlyph struct { type FontAtlasGlyph struct {
U float32 U float32
V float32 V float32
SizeU float32
SizeV float32
Ascent float32 Ascent float32
Descent float32 Descent float32
@ -73,8 +74,8 @@ func NewFontAtlasFromFont(f *truetype.Font, face font.Face, pointSize uint) (*Fo
assert.T(len(glyphs) > 0, "no glyphs") assert.T(len(glyphs) > 0, "no glyphs")
//Find advance and line height //Find advance and line height
const charPaddingX = 2 const charPaddingX = 4
const charPaddingY = 2 const charPaddingY = 4
charAdvFixed, _ := face.GlyphAdvance('L') charAdvFixed, _ := face.GlyphAdvance('L')
charAdv := charAdvFixed.Ceil() + charPaddingX charAdv := charAdvFixed.Ceil() + charPaddingX
@ -120,7 +121,7 @@ func NewFontAtlasFromFont(f *truetype.Font, face font.Face, pointSize uint) (*Fo
//Create atlas //Create atlas
// atlasSizeXF32 := float32(atlasSizeX) // atlasSizeXF32 := float32(atlasSizeX)
atlasSizeYF32 := float32(atlasSizeY) // atlasSizeYF32 := float32(atlasSizeY)
atlas := &FontAtlas{ atlas := &FontAtlas{
Font: f, Font: f,
Img: image.NewRGBA(image.Rect(0, 0, atlasSizeX, atlasSizeY)), Img: image.NewRGBA(image.Rect(0, 0, atlasSizeX, atlasSizeY)),
@ -128,7 +129,6 @@ func NewFontAtlasFromFont(f *truetype.Font, face font.Face, pointSize uint) (*Fo
Advance: charAdv - charPaddingX, Advance: charAdv - charPaddingX,
LineHeight: lineHeight, LineHeight: lineHeight,
SizeUV: *gglm.NewVec2(float32(charAdv-charPaddingX), float32(lineHeight)),
// SizeUV: *gglm.NewVec2(float32(charAdv-charPaddingX)/atlasSizeXF32, float32(lineHeight)/atlasSizeYF32), // SizeUV: *gglm.NewVec2(float32(charAdv-charPaddingX)/atlasSizeXF32, float32(lineHeight)/atlasSizeYF32),
} }
@ -146,70 +146,58 @@ func NewFontAtlasFromFont(f *truetype.Font, face font.Face, pointSize uint) (*Fo
charsOnLine := 0 charsOnLine := 0
drawer.Dot = fixed.P(atlas.Advance+charPaddingX, lineHeight) drawer.Dot = fixed.P(atlas.Advance+charPaddingX, lineHeight)
const drawBoundingBoxes = false
drawHorizontalLines := true for currGlyphCount, g := range glyphs {
drawVerticalLines := true
for _, g := range glyphs {
gBounds, _, _ := face.GlyphBounds(g) gBounds, _, _ := face.GlyphBounds(g)
ascent := absFixedI26_6(gBounds.Min.Y) bearingX := gBounds.Min.X
descent := absFixedI26_6(gBounds.Max.Y) ascentAbsFixed := absFixedI26_6(gBounds.Min.Y)
bearingX := absFixedI26_6(gBounds.Min.X) descentAbsFixed := absFixedI26_6(gBounds.Max.Y)
gWidth := gBounds.Min.X + gBounds.Max.X
gTopLeft := image.Point{
X: (drawer.Dot.X + bearingX).Floor(),
Y: (drawer.Dot.Y - ascentAbsFixed).Floor(),
}
gBotRight := image.Point{
X: (drawer.Dot.X + gWidth).Ceil(),
Y: (drawer.Dot.Y + descentAbsFixed).Ceil(),
}
atlas.Glyphs[g] = FontAtlasGlyph{ atlas.Glyphs[g] = FontAtlasGlyph{
U: float32((drawer.Dot.X).Floor()), U: float32(gTopLeft.X) - 1,
V: (atlasSizeYF32 - float32((drawer.Dot.Y).Ceil())), V: float32(atlasSizeY - gBotRight.Y),
// U: float32((drawer.Dot.X).Floor()) / atlasSizeXF32, SizeU: float32(gBotRight.X - gTopLeft.X),
// V: (atlasSizeYF32 - float32((drawer.Dot.Y).Ceil())) / atlasSizeYF32, SizeV: float32(gBotRight.Y - gTopLeft.Y),
Ascent: float32(ascent.Ceil()), Ascent: float32(ascentAbsFixed.Ceil()),
Descent: float32(descent.Ceil()), Descent: float32(descentAbsFixed.Ceil()),
BearingX: float32(bearingX.Ceil()), BearingX: float32(bearingX.Ceil()),
} }
//Get glyph to draw but undo any applied descent so that the glyph is drawn sitting on the line exactly. imgRect, mask, maskp, gAdvance, _ := face.Glyph(drawer.Dot, g)
//Bearing will be applied correctly but descent will be the responsibility of the positioning code if gAdvance == 0 {
imgRect, mask, maskp, _, _ := face.Glyph(drawer.Dot, g) fmt.Printf("Got advance of %s for char with code 0x%04x\n", gAdvance.String(), g)
if imgRect.Max.Y > drawer.Dot.Y.Ceil() { continue
diff := imgRect.Max.Y - drawer.Dot.Y.Ceil()
imgRect.Min.Y -= diff
imgRect.Max.Y -= diff
} }
if drawVerticalLines { if drawBoundingBoxes {
rectCopy := imgRect
rectCopy.Min.Y = 0 rect := image.Rectangle{
rectCopy.Max.Y = drawer.Dst.Bounds().Max.Y Min: gTopLeft,
rectCopy.Max.X = rectCopy.Min.X + 1 Max: gBotRight,
oldPos := drawer.Dot }
drawer.Dot.Y = 0 drawRectOutline(atlas.Img, rect, color.NRGBA{B: 255, A: 128})
// fmt.Printf("Drawing with maskP %s\n", maskp.String())
draw.Draw(drawer.Dst, rectCopy, image.NewUniform(color.NRGBA{G: 255, A: 255}), image.Point{}, draw.Over)
drawer.Dot = oldPos
} }
//Draw glyph and advance dot //Draw glyph and advance dot
draw.DrawMask(drawer.Dst, imgRect, drawer.Src, image.Point{}, mask, maskp, draw.Over) draw.DrawMask(drawer.Dst, imgRect, drawer.Src, image.Point{}, mask, maskp, draw.Over)
drawer.Dot.X += fixed.I(atlas.Advance) + charPaddingXFixed drawer.Dot.X += gWidth + charPaddingXFixed
charsOnLine++ charsOnLine++
if charsOnLine == charsPerLine { if charsOnLine == charsPerLine || currGlyphCount == len(glyphs)-1 {
if drawHorizontalLines {
rectCopy := imgRect
rectCopy.Min.X = 0
rectCopy.Max.X = drawer.Dst.Bounds().Max.X
// rectCopy.Min.Y += (lineHeightFixed + charPaddingYFixed).Floor() * 1
rectCopy.Max.Y = rectCopy.Min.Y + 1
oldPos := drawer.Dot
drawer.Dot.X = 0
draw.Draw(drawer.Dst, rectCopy, image.NewUniform(color.NRGBA{G: 255, A: 255}), image.Point{}, draw.Over)
drawer.Dot = oldPos
drawVerticalLines = false
}
charsOnLine = 0 charsOnLine = 0
drawer.Dot.X = fixed.I(atlas.Advance) + charPaddingXFixed drawer.Dot.X = fixed.I(atlas.Advance) + charPaddingXFixed
@ -220,6 +208,82 @@ func NewFontAtlasFromFont(f *truetype.Font, face font.Face, pointSize uint) (*Fo
return atlas, nil return atlas, nil
} }
func drawRectOutline(img *image.RGBA, rect image.Rectangle, color color.NRGBA) {
rowPixCount := img.Stride / 4
topLeft := img.PixOffset(rect.Min.X, rect.Min.Y)
botRight := img.PixOffset(rect.Max.X, rect.Max.Y)
for i := topLeft; i <= botRight; i += 4 {
pixel := i / 4
y := pixel / rowPixCount
x := pixel - y*rowPixCount
if x >= rect.Min.X && x <= rect.Max.X && y >= rect.Min.Y && y <= rect.Max.Y {
if x == rect.Min.X || x == rect.Max.X || y == rect.Min.Y || y == rect.Max.Y {
img.Pix[i+3] = color.A
img.Pix[i+2] = color.B
img.Pix[i+1] = color.G
img.Pix[i+0] = color.R
}
}
}
}
func DrawRect(img *image.RGBA, rect image.Rectangle, color color.NRGBA) {
rowPixCount := img.Stride / 4
topLeft := img.PixOffset(rect.Min.X, rect.Min.Y)
botRight := img.PixOffset(rect.Max.X, rect.Max.Y)
//Draw top line
for i := topLeft; i <= botRight; i += 4 {
pixel := i / 4
y := pixel / rowPixCount
x := pixel - y*rowPixCount
if x >= rect.Min.X && x <= rect.Max.X && y >= rect.Min.Y && y <= rect.Max.Y {
img.Pix[i+3] = color.A
img.Pix[i+2] = color.B
img.Pix[i+1] = color.G
img.Pix[i+0] = color.R
}
}
}
func DrawVerticalLine(img *image.RGBA, posX int, color color.NRGBA) {
rowLength := img.Stride
start := img.PixOffset(posX, 0)
for i := start; i < len(img.Pix); i += rowLength {
img.Pix[i+3] = color.A
img.Pix[i+2] = color.B
img.Pix[i+1] = color.G
img.Pix[i+0] = color.R
}
}
func DrawHorizontalLine(img *image.RGBA, posY int, color color.NRGBA) {
rowLength := img.Stride
start := img.PixOffset(0, posY)
//Horizontal line
for i := start; i < start+rowLength; i += 4 {
img.Pix[i+3] = color.A
img.Pix[i+2] = color.B
img.Pix[i+1] = color.G
img.Pix[i+0] = color.R
}
}
func SaveImgToPNG(img image.Image, file string) error { func SaveImgToPNG(img image.Image, file string) error {
outFile, err := os.Create(file) outFile, err := os.Create(file)

View File

@ -2,7 +2,6 @@ package glyphs
import ( import (
"errors" "errors"
"math"
"unicode" "unicode"
"github.com/bloeys/gglm/gglm" "github.com/bloeys/gglm/gglm"
@ -17,7 +16,7 @@ import (
const ( const (
MaxGlyphsPerBatch = 16384 MaxGlyphsPerBatch = 16384
floatsPerGlyph = 11 floatsPerGlyph = 13
invalidRune = unicode.ReplacementChar invalidRune = unicode.ReplacementChar
) )
@ -69,7 +68,7 @@ func (gr *GlyphRend) DrawTextOpenGLAbs(text string, screenPos *gglm.Vec3, color
pos := screenPos.Clone() pos := screenPos.Clone()
advanceF32 := float32(gr.Atlas.Advance) advanceF32 := float32(gr.Atlas.Advance)
lineHeightF32 := float32(gr.Atlas.LineHeight) lineHeightF32 := float32(gr.Atlas.LineHeight)
scale := gglm.NewVec2(advanceF32, lineHeightF32) // scale := gglm.NewVec2(advanceF32, lineHeightF32)
buffIndex := gr.GlyphCount * floatsPerGlyph buffIndex := gr.GlyphCount * floatsPerGlyph
@ -122,28 +121,37 @@ func (gr *GlyphRend) DrawTextOpenGLAbs(text string, screenPos *gglm.Vec3, color
//The uvs coming in make it so that glyphs are sitting on top of the baseline (no descent) and with horizontal bearing applied. //The uvs coming in make it so that glyphs are sitting on top of the baseline (no descent) and with horizontal bearing applied.
//So to position correctly we move them down by the descent amount. //So to position correctly we move them down by the descent amount.
drawPos := *pos drawPos := *pos
drawPos.SetX(drawPos.X()) drawPos.SetX(drawPos.X() + g.BearingX)
drawPos.SetY(drawPos.Y() - g.Descent) drawPos.SetY(drawPos.Y() - g.Descent)
//Add the glyph information to the vbo //Add the glyph information to the vbo
//UV //UV
gr.GlyphVBO[buffIndex+0] = g.U gr.GlyphVBO[buffIndex+0] = g.U
gr.GlyphVBO[buffIndex+1] = g.V gr.GlyphVBO[buffIndex+1] = g.V
buffIndex += 2
//UVSize
gr.GlyphVBO[buffIndex+0] = g.SizeU
gr.GlyphVBO[buffIndex+1] = g.SizeV
buffIndex += 2
//Color //Color
gr.GlyphVBO[buffIndex+2] = color.R() gr.GlyphVBO[buffIndex+0] = color.R()
gr.GlyphVBO[buffIndex+3] = color.G() gr.GlyphVBO[buffIndex+1] = color.G()
gr.GlyphVBO[buffIndex+4] = color.B() gr.GlyphVBO[buffIndex+2] = color.B()
gr.GlyphVBO[buffIndex+5] = color.A() gr.GlyphVBO[buffIndex+3] = color.A()
buffIndex += 4
//Model Pos //Model Pos
gr.GlyphVBO[buffIndex+6] = drawPos.X() gr.GlyphVBO[buffIndex+0] = drawPos.X()
gr.GlyphVBO[buffIndex+7] = drawPos.Y() gr.GlyphVBO[buffIndex+1] = drawPos.Y()
gr.GlyphVBO[buffIndex+8] = drawPos.Z() gr.GlyphVBO[buffIndex+2] = drawPos.Z()
buffIndex += 3
//Model Scale //Model Scale
gr.GlyphVBO[buffIndex+9] = scale.X() gr.GlyphVBO[buffIndex+0] = g.SizeU
gr.GlyphVBO[buffIndex+10] = scale.Y() gr.GlyphVBO[buffIndex+1] = g.SizeV
buffIndex += 2
gr.GlyphCount++ gr.GlyphCount++
pos.AddX(advanceF32) pos.AddX(advanceF32)
@ -152,8 +160,6 @@ func (gr *GlyphRend) DrawTextOpenGLAbs(text string, screenPos *gglm.Vec3, color
if gr.GlyphCount == MaxGlyphsPerBatch { if gr.GlyphCount == MaxGlyphsPerBatch {
gr.Draw() gr.Draw()
buffIndex = 0 buffIndex = 0
} else {
buffIndex += floatsPerGlyph
} }
prevRune = r prevRune = r
@ -192,28 +198,37 @@ func (gr *GlyphRend) DrawTextOpenGLAbs(text string, screenPos *gglm.Vec3, color
//The uvs coming in make it so that glyphs are sitting on top of the baseline (no descent) and with horizontal bearing applied. //The uvs coming in make it so that glyphs are sitting on top of the baseline (no descent) and with horizontal bearing applied.
//So to position correctly we move them down by the descent amount. //So to position correctly we move them down by the descent amount.
drawPos := *pos drawPos := *pos
drawPos.SetX(drawPos.X()) drawPos.SetX(drawPos.X() + g.BearingX)
drawPos.SetY(drawPos.Y() - g.Descent) drawPos.SetY(drawPos.Y() - g.Descent)
//Add the glyph information to the vbo //Add the glyph information to the vbo
//UV //UV
gr.GlyphVBO[buffIndex+0] = g.U gr.GlyphVBO[buffIndex+0] = g.U
gr.GlyphVBO[buffIndex+1] = g.V gr.GlyphVBO[buffIndex+1] = g.V
buffIndex += 2
//UVSize
gr.GlyphVBO[buffIndex+0] = g.SizeU
gr.GlyphVBO[buffIndex+1] = g.SizeV
buffIndex += 2
//Color //Color
gr.GlyphVBO[buffIndex+2] = color.R() gr.GlyphVBO[buffIndex+0] = color.R()
gr.GlyphVBO[buffIndex+3] = color.G() gr.GlyphVBO[buffIndex+1] = color.G()
gr.GlyphVBO[buffIndex+4] = color.B() gr.GlyphVBO[buffIndex+2] = color.B()
gr.GlyphVBO[buffIndex+5] = color.A() gr.GlyphVBO[buffIndex+3] = color.A()
buffIndex += 4
//Model Pos //Model Pos
gr.GlyphVBO[buffIndex+6] = roundF32(drawPos.X()) gr.GlyphVBO[buffIndex+0] = drawPos.X()
gr.GlyphVBO[buffIndex+7] = roundF32(drawPos.Y()) gr.GlyphVBO[buffIndex+1] = drawPos.Y()
gr.GlyphVBO[buffIndex+8] = drawPos.Z() gr.GlyphVBO[buffIndex+2] = drawPos.Z()
buffIndex += 3
//Model Scale //Model Scale
gr.GlyphVBO[buffIndex+9] = scale.X() gr.GlyphVBO[buffIndex+0] = g.SizeU
gr.GlyphVBO[buffIndex+10] = scale.Y() gr.GlyphVBO[buffIndex+1] = g.SizeV
buffIndex += 2
gr.GlyphCount++ gr.GlyphCount++
pos.AddX(advanceF32) pos.AddX(advanceF32)
@ -222,8 +237,6 @@ func (gr *GlyphRend) DrawTextOpenGLAbs(text string, screenPos *gglm.Vec3, color
if gr.GlyphCount == MaxGlyphsPerBatch { if gr.GlyphCount == MaxGlyphsPerBatch {
gr.Draw() gr.Draw()
buffIndex = 0 buffIndex = 0
} else {
buffIndex += floatsPerGlyph
} }
prevRune = r prevRune = r
@ -450,7 +463,7 @@ func (gr *GlyphRend) updateFontAtlasTexture() error {
//Update material //Update material
gr.GlyphMat.DiffuseTex = gr.AtlasTex.TexID gr.GlyphMat.DiffuseTex = gr.AtlasTex.TexID
gr.GlyphMat.SetUnifVec2("sizeUV", &gr.Atlas.SizeUV) // gr.GlyphMat.SetUnifVec2("sizeUV", &gr.Atlas.SizeUV)
return nil return nil
} }
@ -527,6 +540,7 @@ func NewGlyphRend(fontFile string, fontOptions *truetype.Options, screenWidth, s
gr.InstancedBuf.SetLayout( gr.InstancedBuf.SetLayout(
buffers.Element{ElementType: buffers.DataTypeVec2}, //UV0 buffers.Element{ElementType: buffers.DataTypeVec2}, //UV0
buffers.Element{ElementType: buffers.DataTypeVec2}, //UVSize
buffers.Element{ElementType: buffers.DataTypeVec4}, //Color buffers.Element{ElementType: buffers.DataTypeVec4}, //Color
buffers.Element{ElementType: buffers.DataTypeVec3}, //ModelPos buffers.Element{ElementType: buffers.DataTypeVec3}, //ModelPos
buffers.Element{ElementType: buffers.DataTypeVec2}, //ModelScale buffers.Element{ElementType: buffers.DataTypeVec2}, //ModelScale
@ -542,21 +556,26 @@ func NewGlyphRend(fontFile string, fontOptions *truetype.Options, screenWidth, s
gl.VertexAttribPointer(1, uvEle.ElementType.CompCount(), uvEle.ElementType.GLType(), false, gr.InstancedBuf.Stride, gl.PtrOffset(uvEle.Offset)) gl.VertexAttribPointer(1, uvEle.ElementType.CompCount(), uvEle.ElementType.GLType(), false, gr.InstancedBuf.Stride, gl.PtrOffset(uvEle.Offset))
gl.VertexAttribDivisor(1, 1) gl.VertexAttribDivisor(1, 1)
colorEle := layout[1] uvSize := layout[1]
gl.EnableVertexAttribArray(2) gl.EnableVertexAttribArray(2)
gl.VertexAttribPointer(2, colorEle.ElementType.CompCount(), colorEle.ElementType.GLType(), false, gr.InstancedBuf.Stride, gl.PtrOffset(colorEle.Offset)) gl.VertexAttribPointer(2, uvSize.ElementType.CompCount(), uvSize.ElementType.GLType(), false, gr.InstancedBuf.Stride, gl.PtrOffset(uvSize.Offset))
gl.VertexAttribDivisor(2, 1) gl.VertexAttribDivisor(2, 1)
posEle := layout[2] colorEle := layout[2]
gl.EnableVertexAttribArray(3) gl.EnableVertexAttribArray(3)
gl.VertexAttribPointer(3, posEle.ElementType.CompCount(), posEle.ElementType.GLType(), false, gr.InstancedBuf.Stride, gl.PtrOffset(posEle.Offset)) gl.VertexAttribPointer(3, colorEle.ElementType.CompCount(), colorEle.ElementType.GLType(), false, gr.InstancedBuf.Stride, gl.PtrOffset(colorEle.Offset))
gl.VertexAttribDivisor(3, 1) gl.VertexAttribDivisor(3, 1)
scaleEle := layout[3] posEle := layout[3]
gl.EnableVertexAttribArray(4) gl.EnableVertexAttribArray(4)
gl.VertexAttribPointer(4, scaleEle.ElementType.CompCount(), scaleEle.ElementType.GLType(), false, gr.InstancedBuf.Stride, gl.PtrOffset(scaleEle.Offset)) gl.VertexAttribPointer(4, posEle.ElementType.CompCount(), posEle.ElementType.GLType(), false, gr.InstancedBuf.Stride, gl.PtrOffset(posEle.Offset))
gl.VertexAttribDivisor(4, 1) gl.VertexAttribDivisor(4, 1)
scaleEle := layout[4]
gl.EnableVertexAttribArray(5)
gl.VertexAttribPointer(5, scaleEle.ElementType.CompCount(), scaleEle.ElementType.GLType(), false, gr.InstancedBuf.Stride, gl.PtrOffset(scaleEle.Offset))
gl.VertexAttribDivisor(5, 1)
gl.BindBuffer(gl.ARRAY_BUFFER, 0) gl.BindBuffer(gl.ARRAY_BUFFER, 0)
gr.InstancedBuf.UnBind() gr.InstancedBuf.UnBind()
@ -572,7 +591,3 @@ func NewGlyphRend(fontFile string, fontOptions *truetype.Options, screenWidth, s
return gr, nil return gr, nil
} }
func roundF32(x float32) float32 {
return float32(math.Round(float64(x)))
}

5
imgui.ini Executable file
View File

@ -0,0 +1,5 @@
[Window][Debug##Default]
Pos=814,53
Size=405,63
Collapsed=0

14
main.go
View File

@ -11,8 +11,10 @@ import (
"github.com/bloeys/nmage/meshes" "github.com/bloeys/nmage/meshes"
"github.com/bloeys/nmage/renderer/rend3dgl" "github.com/bloeys/nmage/renderer/rend3dgl"
nmageimgui "github.com/bloeys/nmage/ui/imgui" nmageimgui "github.com/bloeys/nmage/ui/imgui"
"github.com/bloeys/nterm/assert"
"github.com/bloeys/nterm/glyphs" "github.com/bloeys/nterm/glyphs"
"github.com/golang/freetype/truetype" "github.com/golang/freetype/truetype"
"github.com/inkyblackness/imgui-go/v4"
"github.com/veandco/go-sdl2/sdl" "github.com/veandco/go-sdl2/sdl"
"golang.org/x/image/font" "golang.org/x/image/font"
) )
@ -107,6 +109,7 @@ func (p *program) Init() {
p.handleWindowResize() p.handleWindowResize()
// runs := p.GlyphRend.GetTextRuns("hello there يا friend. أسمي عمر wow") // runs := p.GlyphRend.GetTextRuns("hello there يا friend. أسمي عمر wow")
// runs := p.GlyphRend.GetTextRuns("hello there my friend!")
// fmt.Printf("%+v\n", runs) // fmt.Printf("%+v\n", runs)
// for _, r := range runs { // for _, r := range runs {
// fmt.Printf("%s;\n", string(r)) // fmt.Printf("%s;\n", string(r))
@ -164,7 +167,14 @@ func (p *program) Update() {
if input.KeyClicked(sdl.K_SPACE) { if input.KeyClicked(sdl.K_SPACE) {
p.shouldDrawGrid = !p.shouldDrawGrid p.shouldDrawGrid = !p.shouldDrawGrid
} }
imgui.InputText("", &textToShow)
if len(textToShow) > 0 {
assert.T(len(textToShow) == len(p.GlyphRend.GetTextRuns(textToShow)[0]), "??")
} }
}
var textToShow = "Hello there my friend"
var xOff float32 = 0 var xOff float32 = 0
var yOff float32 = 0 var yOff float32 = 0
@ -198,8 +208,10 @@ func (p *program) Render() {
} }
textColor := gglm.NewVec4(r, g, b, 1) textColor := gglm.NewVec4(r, g, b, 1)
str := textToShow
// str := "مرحبا بك my friend" // str := "مرحبا بك my friend"
str := " hello there يا friend. سمي عمر wow" // str := "my, friend"
// str := " hello there يا friend. سمي عمر wow"
// str := " ijojo\n\n Hello there, friend|. pq?\n ABCDEFG\tHIJKLMNOPQRSTUVWXYZ\nمرحبا بك" // str := " ijojo\n\n Hello there, friend|. pq?\n ABCDEFG\tHIJKLMNOPQRSTUVWXYZ\nمرحبا بك"
// str := " ijojo\n\n Hello there, friend|. pq?\n ABCDEFG\tHIJKLMNOPQRSTUVWXYZ" // str := " ijojo\n\n Hello there, friend|. pq?\n ABCDEFG\tHIJKLMNOPQRSTUVWXYZ"

View File

@ -6,9 +6,10 @@ layout(location=0) in vec3 aVertPos;
//Instanced //Instanced
layout(location=1) in vec2 aUV0; layout(location=1) in vec2 aUV0;
layout(location=2) in vec4 aVertColor; layout(location=2) in vec2 aUVSize;
layout(location=3) in vec3 aModelPos; layout(location=3) in vec4 aVertColor;
layout(location=4) in vec2 aModelScale; layout(location=4) in vec3 aModelPos;
layout(location=5) in vec2 aModelScale;
out vec2 v2fUV0; out vec2 v2fUV0;
out vec4 v2fColor; out vec4 v2fColor;
@ -16,7 +17,6 @@ out vec3 v2fFragPos;
uniform mat4 projViewMat; uniform mat4 projViewMat;
uniform vec2 modelSize; uniform vec2 modelSize;
uniform vec2 sizeUV;
void main() void main()
{ {
@ -27,7 +27,7 @@ void main()
aModelPos.x, aModelPos.y, aModelPos.z, 1.0 aModelPos.x, aModelPos.y, aModelPos.z, 1.0
); );
v2fUV0 = aUV0 + aVertPos.xy * sizeUV; v2fUV0 = aUV0 + aVertPos.xy * aUVSize;
v2fColor = aVertColor; v2fColor = aVertColor;
gl_Position = projViewMat * modelMat * vec4(aVertPos, 1.0); gl_Position = projViewMat * modelMat * vec4(aVertPos, 1.0);