mirror of
https://github.com/bloeys/nterm.git
synced 2025-12-29 14:38:19 +00:00
Disable DepthTest when drawing glyphs+move shared glyph draw logic
With depth testing enabled nearby glyphs can z-fight and so some pixels might be discarded before fragment shader even runs, which causes us to see only part of a letter. As such we always draw with blending enabled and depth testing disabled so that any overlaps simply causes the char pixels to show.
This commit is contained in:
@ -2,7 +2,6 @@ package glyphs
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
"image/draw"
|
"image/draw"
|
||||||
@ -146,7 +145,7 @@ 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
|
const drawBoundingBoxes bool = false
|
||||||
|
|
||||||
for currGlyphCount, g := range glyphs {
|
for currGlyphCount, g := range glyphs {
|
||||||
|
|
||||||
@ -177,11 +176,7 @@ func NewFontAtlasFromFont(f *truetype.Font, face font.Face, pointSize uint) (*Fo
|
|||||||
BearingX: float32(bearingX.Ceil()),
|
BearingX: float32(bearingX.Ceil()),
|
||||||
}
|
}
|
||||||
|
|
||||||
imgRect, mask, maskp, gAdvance, _ := face.Glyph(drawer.Dot, g)
|
imgRect, mask, maskp, _, _ := face.Glyph(drawer.Dot, g)
|
||||||
if gAdvance == 0 {
|
|
||||||
fmt.Printf("Got advance of %s for char with code 0x%04x\n", gAdvance.String(), g)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if drawBoundingBoxes {
|
if drawBoundingBoxes {
|
||||||
|
|
||||||
|
|||||||
238
glyphs/glyphs.go
238
glyphs/glyphs.go
@ -68,9 +68,8 @@ 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)
|
|
||||||
|
|
||||||
buffIndex := gr.GlyphCount * floatsPerGlyph
|
bufIndex := gr.GlyphCount * floatsPerGlyph
|
||||||
|
|
||||||
for _, run := range runs {
|
for _, run := range runs {
|
||||||
|
|
||||||
@ -91,155 +90,15 @@ func (gr *GlyphRend) DrawTextOpenGLAbs(text string, screenPos *gglm.Vec3, color
|
|||||||
if isLtr {
|
if isLtr {
|
||||||
|
|
||||||
for i := 0; i < len(rs); i++ {
|
for i := 0; i < len(rs); i++ {
|
||||||
|
gr.drawRune(rs, i, prevRune, screenPos, pos, color, advanceF32, lineHeightF32, &bufIndex, isLtr)
|
||||||
r := rs[i]
|
prevRune = rs[i]
|
||||||
if r == '\n' {
|
|
||||||
screenPos.SetY(screenPos.Y() - lineHeightF32)
|
|
||||||
pos = screenPos.Clone()
|
|
||||||
prevRune = r
|
|
||||||
continue
|
|
||||||
} else if r == ' ' {
|
|
||||||
pos.AddX(advanceF32)
|
|
||||||
prevRune = r
|
|
||||||
continue
|
|
||||||
} else if r == '\t' {
|
|
||||||
pos.AddX(advanceF32 * float32(gr.SpacesPerTab))
|
|
||||||
prevRune = r
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var g FontAtlasGlyph
|
|
||||||
if i < len(rs)-1 {
|
|
||||||
//start or middle of sentence
|
|
||||||
g = gr.glyphFromRunes(r, prevRune, rs[i+1])
|
|
||||||
} else {
|
|
||||||
//Last character
|
|
||||||
g = gr.glyphFromRunes(r, prevRune, invalidRune)
|
|
||||||
}
|
|
||||||
|
|
||||||
//See: https://developer.apple.com/library/archive/documentation/TextFonts/Conceptual/CocoaTextArchitecture/Art/glyph_metrics_2x.png
|
|
||||||
//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.
|
|
||||||
drawPos := *pos
|
|
||||||
drawPos.SetX(drawPos.X() + g.BearingX)
|
|
||||||
drawPos.SetY(drawPos.Y() - g.Descent)
|
|
||||||
|
|
||||||
//Add the glyph information to the vbo
|
|
||||||
//UV
|
|
||||||
gr.GlyphVBO[buffIndex+0] = g.U
|
|
||||||
gr.GlyphVBO[buffIndex+1] = g.V
|
|
||||||
buffIndex += 2
|
|
||||||
|
|
||||||
//UVSize
|
|
||||||
gr.GlyphVBO[buffIndex+0] = g.SizeU
|
|
||||||
gr.GlyphVBO[buffIndex+1] = g.SizeV
|
|
||||||
buffIndex += 2
|
|
||||||
|
|
||||||
//Color
|
|
||||||
gr.GlyphVBO[buffIndex+0] = color.R()
|
|
||||||
gr.GlyphVBO[buffIndex+1] = color.G()
|
|
||||||
gr.GlyphVBO[buffIndex+2] = color.B()
|
|
||||||
gr.GlyphVBO[buffIndex+3] = color.A()
|
|
||||||
buffIndex += 4
|
|
||||||
|
|
||||||
//Model Pos
|
|
||||||
gr.GlyphVBO[buffIndex+0] = drawPos.X()
|
|
||||||
gr.GlyphVBO[buffIndex+1] = drawPos.Y()
|
|
||||||
gr.GlyphVBO[buffIndex+2] = drawPos.Z()
|
|
||||||
buffIndex += 3
|
|
||||||
|
|
||||||
//Model Scale
|
|
||||||
gr.GlyphVBO[buffIndex+0] = g.SizeU
|
|
||||||
gr.GlyphVBO[buffIndex+1] = g.SizeV
|
|
||||||
buffIndex += 2
|
|
||||||
|
|
||||||
gr.GlyphCount++
|
|
||||||
pos.AddX(advanceF32)
|
|
||||||
|
|
||||||
//If we fill the buffer we issue a draw call
|
|
||||||
if gr.GlyphCount == MaxGlyphsPerBatch {
|
|
||||||
gr.Draw()
|
|
||||||
buffIndex = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
prevRune = r
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
for i := len(rs) - 1; i >= 0; i-- {
|
for i := len(rs) - 1; i >= 0; i-- {
|
||||||
|
gr.drawRune(rs, i, prevRune, screenPos, pos, color, advanceF32, lineHeightF32, &bufIndex, isLtr)
|
||||||
r := rs[i]
|
prevRune = rs[i]
|
||||||
if r == '\n' {
|
|
||||||
screenPos.SetY(screenPos.Y() - lineHeightF32)
|
|
||||||
pos = screenPos.Clone()
|
|
||||||
prevRune = r
|
|
||||||
continue
|
|
||||||
} else if r == ' ' {
|
|
||||||
pos.AddX(advanceF32)
|
|
||||||
prevRune = r
|
|
||||||
continue
|
|
||||||
} else if r == '\t' {
|
|
||||||
pos.AddX(advanceF32 * float32(gr.SpacesPerTab))
|
|
||||||
prevRune = r
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var g FontAtlasGlyph
|
|
||||||
if i > 0 {
|
|
||||||
//start or middle of sentence
|
|
||||||
g = gr.glyphFromRunes(r, rs[i-1], prevRune)
|
|
||||||
} else {
|
|
||||||
//Last character
|
|
||||||
g = gr.glyphFromRunes(r, invalidRune, prevRune)
|
|
||||||
}
|
|
||||||
|
|
||||||
//See: https://developer.apple.com/library/archive/documentation/TextFonts/Conceptual/CocoaTextArchitecture/Art/glyph_metrics_2x.png
|
|
||||||
//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.
|
|
||||||
drawPos := *pos
|
|
||||||
drawPos.SetX(drawPos.X() + g.BearingX)
|
|
||||||
drawPos.SetY(drawPos.Y() - g.Descent)
|
|
||||||
|
|
||||||
//Add the glyph information to the vbo
|
|
||||||
//UV
|
|
||||||
gr.GlyphVBO[buffIndex+0] = g.U
|
|
||||||
gr.GlyphVBO[buffIndex+1] = g.V
|
|
||||||
buffIndex += 2
|
|
||||||
|
|
||||||
//UVSize
|
|
||||||
gr.GlyphVBO[buffIndex+0] = g.SizeU
|
|
||||||
gr.GlyphVBO[buffIndex+1] = g.SizeV
|
|
||||||
buffIndex += 2
|
|
||||||
|
|
||||||
//Color
|
|
||||||
gr.GlyphVBO[buffIndex+0] = color.R()
|
|
||||||
gr.GlyphVBO[buffIndex+1] = color.G()
|
|
||||||
gr.GlyphVBO[buffIndex+2] = color.B()
|
|
||||||
gr.GlyphVBO[buffIndex+3] = color.A()
|
|
||||||
buffIndex += 4
|
|
||||||
|
|
||||||
//Model Pos
|
|
||||||
gr.GlyphVBO[buffIndex+0] = drawPos.X()
|
|
||||||
gr.GlyphVBO[buffIndex+1] = drawPos.Y()
|
|
||||||
gr.GlyphVBO[buffIndex+2] = drawPos.Z()
|
|
||||||
buffIndex += 3
|
|
||||||
|
|
||||||
//Model Scale
|
|
||||||
gr.GlyphVBO[buffIndex+0] = g.SizeU
|
|
||||||
gr.GlyphVBO[buffIndex+1] = g.SizeV
|
|
||||||
buffIndex += 2
|
|
||||||
|
|
||||||
gr.GlyphCount++
|
|
||||||
pos.AddX(advanceF32)
|
|
||||||
|
|
||||||
//If we fill the buffer we issue a draw call
|
|
||||||
if gr.GlyphCount == MaxGlyphsPerBatch {
|
|
||||||
gr.Draw()
|
|
||||||
buffIndex = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
prevRune = r
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -247,8 +106,92 @@ func (gr *GlyphRend) DrawTextOpenGLAbs(text string, screenPos *gglm.Vec3, color
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gr *GlyphRend) drawRune(rs []rune, i int, prevRune rune, screenPos, pos *gglm.Vec3, color *gglm.Vec4, advanceF32, lineHeightF32 float32, bufIndex *uint32, isLtr bool) {
|
||||||
|
|
||||||
|
r := rs[i]
|
||||||
|
if r == '\n' {
|
||||||
|
screenPos.SetY(screenPos.Y() - lineHeightF32)
|
||||||
|
*pos = *screenPos.Clone()
|
||||||
|
// prevRune = r
|
||||||
|
return
|
||||||
|
} else if r == ' ' {
|
||||||
|
pos.AddX(advanceF32)
|
||||||
|
// prevRune = r
|
||||||
|
return
|
||||||
|
} else if r == '\t' {
|
||||||
|
pos.AddX(advanceF32 * float32(gr.SpacesPerTab))
|
||||||
|
// prevRune = r
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var g FontAtlasGlyph
|
||||||
|
if isLtr {
|
||||||
|
if i < len(rs)-1 {
|
||||||
|
//start or middle of sentence
|
||||||
|
g = gr.glyphFromRunes(r, prevRune, rs[i+1])
|
||||||
|
} else {
|
||||||
|
//Last character
|
||||||
|
g = gr.glyphFromRunes(r, prevRune, invalidRune)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if i > 0 {
|
||||||
|
//start or middle of sentence
|
||||||
|
g = gr.glyphFromRunes(r, rs[i-1], prevRune)
|
||||||
|
} else {
|
||||||
|
//Last character
|
||||||
|
g = gr.glyphFromRunes(r, invalidRune, prevRune)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//We must adjust char positioning according to: https://developer.apple.com/library/archive/documentation/TextFonts/Conceptual/CocoaTextArchitecture/Art/glyph_metrics_2x.png
|
||||||
|
drawPos := *pos
|
||||||
|
drawPos.SetX(drawPos.X() + g.BearingX)
|
||||||
|
drawPos.SetY(drawPos.Y() - g.Descent)
|
||||||
|
|
||||||
|
//Add the glyph information to the vbo
|
||||||
|
//UV
|
||||||
|
gr.GlyphVBO[*bufIndex+0] = g.U
|
||||||
|
gr.GlyphVBO[*bufIndex+1] = g.V
|
||||||
|
*bufIndex += 2
|
||||||
|
|
||||||
|
//UVSize
|
||||||
|
gr.GlyphVBO[*bufIndex+0] = g.SizeU
|
||||||
|
gr.GlyphVBO[*bufIndex+1] = g.SizeV
|
||||||
|
*bufIndex += 2
|
||||||
|
|
||||||
|
//Color
|
||||||
|
gr.GlyphVBO[*bufIndex+0] = color.R()
|
||||||
|
gr.GlyphVBO[*bufIndex+1] = color.G()
|
||||||
|
gr.GlyphVBO[*bufIndex+2] = color.B()
|
||||||
|
gr.GlyphVBO[*bufIndex+3] = color.A()
|
||||||
|
*bufIndex += 4
|
||||||
|
|
||||||
|
//Model Pos
|
||||||
|
gr.GlyphVBO[*bufIndex+0] = drawPos.X()
|
||||||
|
gr.GlyphVBO[*bufIndex+1] = drawPos.Y()
|
||||||
|
gr.GlyphVBO[*bufIndex+2] = drawPos.Z()
|
||||||
|
*bufIndex += 3
|
||||||
|
|
||||||
|
//Model Scale
|
||||||
|
gr.GlyphVBO[*bufIndex+0] = g.SizeU
|
||||||
|
gr.GlyphVBO[*bufIndex+1] = g.SizeV
|
||||||
|
*bufIndex += 2
|
||||||
|
|
||||||
|
gr.GlyphCount++
|
||||||
|
pos.AddX(advanceF32)
|
||||||
|
|
||||||
|
//If we fill the buffer we issue a draw call
|
||||||
|
if gr.GlyphCount == MaxGlyphsPerBatch {
|
||||||
|
gr.Draw()
|
||||||
|
*bufIndex = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// prevRune = r
|
||||||
|
}
|
||||||
|
|
||||||
func (gr *GlyphRend) GetTextRuns(t string) [][]rune {
|
func (gr *GlyphRend) GetTextRuns(t string) [][]rune {
|
||||||
|
|
||||||
|
//PERF: Might be better to pass a [][]rune buffer to avoid allocating on the heap
|
||||||
rs := []rune(t)
|
rs := []rune(t)
|
||||||
|
|
||||||
if len(rs) == 0 {
|
if len(rs) == 0 {
|
||||||
@ -405,7 +348,10 @@ func (gr *GlyphRend) Draw() {
|
|||||||
gr.InstancedBuf.Bind()
|
gr.InstancedBuf.Bind()
|
||||||
gr.GlyphMat.Bind()
|
gr.GlyphMat.Bind()
|
||||||
|
|
||||||
|
//We need to disable depth testing so that nearby characters don't occlude each other
|
||||||
|
gl.Disable(gl.DEPTH_TEST)
|
||||||
gl.DrawElementsInstanced(gl.TRIANGLES, gr.GlyphMesh.Buf.IndexBufCount, gl.UNSIGNED_INT, gl.PtrOffset(0), int32(gr.GlyphCount))
|
gl.DrawElementsInstanced(gl.TRIANGLES, gr.GlyphMesh.Buf.IndexBufCount, gl.UNSIGNED_INT, gl.PtrOffset(0), int32(gr.GlyphCount))
|
||||||
|
gl.Enable(gl.DEPTH_TEST)
|
||||||
gr.GlyphCount = 0
|
gr.GlyphCount = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
6
go.mod
6
go.mod
@ -7,11 +7,9 @@ require (
|
|||||||
github.com/bloeys/nmage v0.12.13
|
github.com/bloeys/nmage v0.12.13
|
||||||
github.com/go-gl/gl v0.0.0-20211025173605-bda47ffaa784
|
github.com/go-gl/gl v0.0.0-20211025173605-bda47ffaa784
|
||||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
||||||
|
github.com/inkyblackness/imgui-go/v4 v4.3.0
|
||||||
github.com/veandco/go-sdl2 v0.4.10
|
github.com/veandco/go-sdl2 v0.4.10
|
||||||
golang.org/x/image v0.0.0-20220617043117-41969df76e82
|
golang.org/x/image v0.0.0-20220617043117-41969df76e82
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require github.com/bloeys/assimp-go v0.4.2 // indirect
|
||||||
github.com/bloeys/assimp-go v0.4.2 // indirect
|
|
||||||
github.com/inkyblackness/imgui-go/v4 v4.3.0 // indirect
|
|
||||||
)
|
|
||||||
|
|||||||
12
main.go
12
main.go
@ -11,7 +11,6 @@ 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/inkyblackness/imgui-go/v4"
|
||||||
@ -60,7 +59,7 @@ func main() {
|
|||||||
rend: rend,
|
rend: rend,
|
||||||
imguiInfo: nmageimgui.NewImGUI(),
|
imguiInfo: nmageimgui.NewImGUI(),
|
||||||
|
|
||||||
FontSize: 20,
|
FontSize: 14,
|
||||||
}
|
}
|
||||||
|
|
||||||
p.win.EventCallbacks = append(p.win.EventCallbacks, func(e sdl.Event) {
|
p.win.EventCallbacks = append(p.win.EventCallbacks, func(e sdl.Event) {
|
||||||
@ -92,7 +91,7 @@ func (p *program) Init() {
|
|||||||
fmt.Printf("DPI: %f, font size: %d\n", dpi, p.FontSize)
|
fmt.Printf("DPI: %f, font size: %d\n", dpi, p.FontSize)
|
||||||
|
|
||||||
w, h := p.win.SDLWin.GetSize()
|
w, h := p.win.SDLWin.GetSize()
|
||||||
p.GlyphRend, err = glyphs.NewGlyphRend("./res/fonts/Consolas.ttf", &truetype.Options{Size: float64(p.FontSize), DPI: p.Dpi, SubPixelsX: subPixelX, SubPixelsY: subPixelY, Hinting: hinting}, w, h)
|
p.GlyphRend, err = glyphs.NewGlyphRend("./res/fonts/KawkabMono-Regular.ttf", &truetype.Options{Size: float64(p.FontSize), DPI: p.Dpi, SubPixelsX: subPixelX, SubPixelsY: subPixelY, Hinting: hinting}, w, h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("Failed to create atlas from font file. Err: " + err.Error())
|
panic("Failed to create atlas from font file. Err: " + err.Error())
|
||||||
}
|
}
|
||||||
@ -169,12 +168,9 @@ func (p *program) Update() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
imgui.InputText("", &textToShow)
|
imgui.InputText("", &textToShow)
|
||||||
if len(textToShow) > 0 {
|
|
||||||
assert.T(len(textToShow) == len(p.GlyphRend.GetTextRuns(textToShow)[0]), "??")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var textToShow = "Hello there my friend"
|
var textToShow = " Hello there يا friend. أسمي عمر wow!"
|
||||||
|
|
||||||
var xOff float32 = 0
|
var xOff float32 = 0
|
||||||
var yOff float32 = 0
|
var yOff float32 = 0
|
||||||
@ -211,7 +207,7 @@ func (p *program) Render() {
|
|||||||
str := textToShow
|
str := textToShow
|
||||||
// str := "مرحبا بك my friend"
|
// str := "مرحبا بك my friend"
|
||||||
// str := "my, friend"
|
// str := "my, friend"
|
||||||
// str := " hello there يا friend. سمي عمر wow"
|
// 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"
|
||||||
|
|
||||||
|
|||||||
@ -46,7 +46,7 @@ uniform sampler2D diffTex;
|
|||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
vec4 texColor = texelFetch(diffTex, ivec2(v2fUV0), 0);
|
vec4 texColor = texelFetch(diffTex, ivec2(v2fUV0), 0);
|
||||||
// vec4 texColor = texture(diffTex, v2fUV0);
|
// This commented out part highlights the full region of the char
|
||||||
// if (texColor.r == 0)
|
// if (texColor.r == 0)
|
||||||
// {
|
// {
|
||||||
// fragColor = vec4(0,1,0,0.25);
|
// fragColor = vec4(0,1,0,0.25);
|
||||||
|
|||||||
Reference in New Issue
Block a user