Rendering from an in-mem glyph grid

This commit is contained in:
bloeys
2022-09-24 07:05:21 +04:00
parent b5797c69f9
commit b6c468a23b
4 changed files with 167 additions and 41 deletions

View File

@ -127,7 +127,7 @@ const (
AnsiCodeOptions_LineAbs
AnsiCodeOptions_ScrollOffset
// This is at the bottom so iota starts at 0
// This is at the bottom so the above iota starts at 0
AnsiCodeOptions_Unknown AnsiCodeOptions = 0
)

128
glyph_grid.go Executable file
View File

@ -0,0 +1,128 @@
package main
import (
"fmt"
"unicode/utf8"
"github.com/bloeys/gglm/gglm"
)
type GridTile struct {
Glyph rune
FgColor gglm.Vec4
BgColor gglm.Vec4
}
type GlyphGrid struct {
CursorX uint
CursorY uint
SizeX uint
SizeY uint
Tiles [][]GridTile
}
func (gg *GlyphGrid) Write(rs []rune, fgColor *gglm.Vec4, bgColor *gglm.Vec4) {
for i := 0; i < len(rs); i++ {
r := rs[i]
gg.Tiles[gg.CursorY][gg.CursorX] = GridTile{
Glyph: r,
FgColor: *fgColor,
BgColor: *bgColor,
}
if !gg.TickCursor(r == '\n') {
break
}
}
}
func (gg *GlyphGrid) Clear() {
for y := 0; y < len(gg.Tiles); y++ {
row := gg.Tiles[y]
for x := 0; x < len(row); x++ {
row[x].Glyph = utf8.RuneError
}
}
gg.SetCursor(0, 0)
}
func (gg *GlyphGrid) SetCursor(x, y uint) {
if x > gg.SizeX || y > gg.SizeY {
panic("cursor position can not be larger than grid size")
}
gg.CursorX = x
gg.CursorY = y
}
func (gg *GlyphGrid) TickCursor(forceDown bool) (success bool) {
if gg.CursorX == gg.SizeX-1 && gg.CursorY == gg.SizeY-1 {
// fmt.Println("trying to advance cursor beyond grid which is not allowed. Keeping cursor at position")
return false
}
if forceDown {
if gg.CursorY == gg.SizeY-1 {
// fmt.Println("trying to move cursor to next line but cursor already at last line. Keeping cursor at position")
return false
}
gg.CursorX = 0
gg.CursorY++
return true
}
gg.CursorX++
if gg.CursorX >= gg.SizeX {
gg.CursorX = 0
gg.CursorY++
}
return true
}
func (gg *GlyphGrid) Print() {
fmt.Println("\n---")
for y := 0; y < len(gg.Tiles); y++ {
row := gg.Tiles[y]
for x := 0; x < len(row); x++ {
if row[x].Glyph == utf8.RuneError {
break
}
fmt.Print(string(row[x].Glyph))
}
fmt.Print("\n")
}
fmt.Println("---")
}
func NewGlyphGrid(width, height uint) *GlyphGrid {
if width == 0 || height == 0 {
panic("glyph grid width and height must be larger than zero")
}
tiles := make([][]GridTile, height)
for i := 0; i < len(tiles); i++ {
tiles[i] = make([]GridTile, width)
}
return &GlyphGrid{
CursorX: 0,
CursorY: 0,
SizeX: width,
SizeY: height,
Tiles: tiles,
}
}

78
main.go
View File

@ -91,7 +91,7 @@ type nterm struct {
scrollPosRel int64
scrollSpd int64
gridTiles [][]GridTile
glyphGrid *GlyphGrid
activeCmd *Cmd
Settings *Settings
@ -116,11 +116,6 @@ const (
defaultScrollSpd = 1
)
type GridTile struct {
glyph rune
color *gglm.Vec4
}
var (
drawGrid bool
drawManyLines = false
@ -155,7 +150,7 @@ func main() {
win: win,
rend: rend,
imguiInfo: nmageimgui.NewImGUI(),
FontSize: 40,
FontSize: 24,
Lines: ring.NewBuffer[Line](defaultLineBufSize),
@ -220,7 +215,8 @@ func (nt *nterm) Init() {
w, h := nt.win.SDLWin.GetSize()
// p.GlyphRend, err = glyphs.NewGlyphRend("./res/fonts/tajawal-regular-var.ttf", &truetype.Options{Size: float64(p.FontSize), DPI: p.Dpi, SubPixelsX: subPixelX, SubPixelsY: subPixelY, Hinting: hinting}, w, h)
nt.GlyphRend, err = glyphs.NewGlyphRend("./res/fonts/alm-fixed.ttf", &truetype.Options{Size: float64(nt.FontSize), DPI: nt.Dpi, SubPixelsX: subPixelX, SubPixelsY: subPixelY, Hinting: hinting}, w, h)
// nt.GlyphRend, err = glyphs.NewGlyphRend("./res/fonts/alm-fixed.ttf", &truetype.Options{Size: float64(nt.FontSize), DPI: nt.Dpi, SubPixelsX: subPixelX, SubPixelsY: subPixelY, Hinting: hinting}, w, h)
nt.GlyphRend, err = glyphs.NewGlyphRend("./res/fonts/CascadiaMono-Regular.ttf", &truetype.Options{Size: float64(nt.FontSize), DPI: nt.Dpi, SubPixelsX: subPixelX, SubPixelsY: subPixelY, Hinting: hinting}, w, h)
if err != nil {
panic("Failed to create atlas from font file. Err: " + err.Error())
}
@ -241,8 +237,12 @@ func (nt *nterm) Init() {
nt.gridMat = materials.NewMaterial("grid", "./res/shaders/grid.glsl")
nt.HandleWindowResize()
//Set initial cursor pos
// Set initial cursor pos
nt.lastCmdCharPos.SetY(nt.GlyphRend.Atlas.LineHeight)
// Init glyph grid
gridWidth, gridHeight := nt.GridSize()
nt.glyphGrid = NewGlyphGrid(uint(gridWidth), uint(gridHeight))
}
func (nt *nterm) Update() {
@ -258,7 +258,7 @@ func (nt *nterm) Update() {
}
//Font sizing
oldFont := nt.FontSize
oldFontSize := nt.FontSize
fontSizeChanged := false
if input.KeyClicked(sdl.K_KP_PLUS) {
nt.FontSize += 2
@ -272,10 +272,12 @@ func (nt *nterm) Update() {
err := nt.GlyphRend.SetFace(&truetype.Options{Size: float64(nt.FontSize), DPI: nt.Dpi, SubPixelsX: subPixelX, SubPixelsY: subPixelY, Hinting: hinting})
if err != nil {
nt.FontSize = oldFont
nt.FontSize = oldFontSize
fmt.Println("Failed to update font face. Err: " + err.Error())
} else {
glyphs.SaveImgToPNG(nt.GlyphRend.Atlas.Img, "./debug-atlas.png")
gridWidth, gridHeight := nt.GridSize()
nt.glyphGrid = NewGlyphGrid(uint(gridWidth), uint(gridHeight))
fmt.Println("New font size:", nt.FontSize, "; New texture size:", nt.GlyphRend.Atlas.Img.Rect.Max.X)
}
}
@ -322,17 +324,38 @@ func (nt *nterm) MainUpdate() {
nt.SepLinePos.SetY(2 * nt.GlyphRend.Atlas.LineHeight)
// Draw textBuf
nt.glyphGrid.Clear()
gw, gh := nt.GridSize()
v1, v2 := nt.textBuf.ViewsFromToRelIndex(uint64(nt.scrollPosRel), uint64(nt.scrollPosRel)+uint64(gw*gh))
nt.lastCmdCharPos.Data = gglm.NewVec3(0, float32(nt.GlyphRend.ScreenHeight)-nt.GlyphRend.Atlas.LineHeight, 0).Data
nt.lastCmdCharPos.Data = nt.DrawTextAnsiCodes(v1, *nt.lastCmdCharPos).Data
nt.lastCmdCharPos.Data = nt.DrawTextAnsiCodes(v2, *nt.lastCmdCharPos).Data
nt.DrawTextAnsiCodesOnGlyphGrid(v1)
nt.DrawTextAnsiCodesOnGlyphGrid(v2)
for y := 0; y < len(nt.glyphGrid.Tiles); y++ {
row := nt.glyphGrid.Tiles[y]
for x := 0; x < len(row); x++ {
g := row[x]
if g.Glyph == utf8.RuneError {
continue
}
nt.GlyphRend.OptValues.BgColor.Data = g.BgColor.Data
nt.lastCmdCharPos.Data = nt.GlyphRend.DrawTextOpenGLAbsRectWithStartPos([]rune{g.Glyph}, nt.lastCmdCharPos, gglm.NewVec3(0, 0, 0), gglm.NewVec2(float32(nt.GlyphRend.ScreenWidth), 2*nt.GlyphRend.Atlas.LineHeight), &g.FgColor).Data
}
}
nt.GlyphRend.OptValues.BgColor.Data = nt.Settings.DefaultBgColor.Data
// Draw cmd buf
nt.lastCmdCharPos.SetX(0)
nt.lastCmdCharPos.SetY(nt.SepLinePos.Y() - nt.GlyphRend.Atlas.LineHeight)
nt.lastCmdCharPos.Data = nt.SyntaxHighlightAndDraw(nt.cmdBuf[:nt.cmdBufLen], *nt.lastCmdCharPos).Data
if input.KeyClicked(sdl.K_F4) {
nt.glyphGrid.Print()
println(nt.glyphGrid.SizeX, nt.glyphGrid.SizeY)
}
}
func (nt *nterm) ReadInputs() {
@ -394,33 +417,13 @@ func (nt *nterm) ReadInputs() {
}
}
func (nt *nterm) DrawTextAnsiCodes(bs []byte, pos gglm.Vec3) gglm.Vec3 {
func (nt *nterm) DrawTextAnsiCodesOnGlyphGrid(bs []byte) {
currFgColor := nt.Settings.DefaultFgColor
currBgColor := nt.Settings.DefaultBgColor
draw := func(rs []rune) {
nt.GlyphRend.OptValues.BgColor.Data = currBgColor.Data
startIndex := 0
for i := 0; i < len(rs); i++ {
r := rs[i]
// @PERF We could probably use bytes.IndexByte here
if r == '\n' {
pos.Data = nt.GlyphRend.DrawTextOpenGLAbsRectWithStartPos(rs[startIndex:i], &pos, gglm.NewVec3(0, 0, 0), gglm.NewVec2(float32(nt.GlyphRend.ScreenWidth), 2*nt.GlyphRend.Atlas.LineHeight), &currFgColor).Data
pos.SetX(0)
pos.AddY(-nt.GlyphRend.Atlas.LineHeight)
startIndex = i + 1
continue
}
}
if startIndex < len(rs) {
pos.Data = nt.GlyphRend.DrawTextOpenGLAbsRectWithStartPos(rs[startIndex:], &pos, gglm.NewVec3(0, 0, 0), gglm.NewVec2(float32(nt.GlyphRend.ScreenWidth), 2*nt.GlyphRend.Atlas.LineHeight), &currFgColor).Data
}
nt.glyphGrid.Write(rs, &currFgColor, &currBgColor)
}
for {
@ -457,8 +460,6 @@ func (nt *nterm) DrawTextAnsiCodes(bs []byte, pos gglm.Vec3) gglm.Vec3 {
// Advance beyond the code chars
bs = bs[index+len(code):]
}
return pos
}
func (nt *nterm) SyntaxHighlightAndDraw(text []rune, pos gglm.Vec3) gglm.Vec3 {
@ -858,9 +859,6 @@ func (nt *nterm) HandleWindowResize() {
projMtx := gglm.Ortho(0, float32(w), float32(h), 0, 0.1, 20)
viewMtx := gglm.LookAt(gglm.NewVec3(0, 0, -10), gglm.NewVec3(0, 0, 0), gglm.NewVec3(0, 1, 0))
nt.gridMat.SetUnifMat4("projViewMat", &projMtx.Mul(viewMtx).Mat4)
gridWidth, gridHeight := nt.GridSize()
nt.gridTiles = make([][]GridTile, gridWidth*gridHeight)
}
func (nt *nterm) WriteToTextBuf(text []byte) {

Binary file not shown.