mirror of
https://github.com/bloeys/nterm.git
synced 2025-12-29 06:28:20 +00:00
Rendering from an in-mem glyph grid
This commit is contained in:
@ -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
128
glyph_grid.go
Executable 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
78
main.go
@ -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) {
|
||||
|
||||
BIN
res/fonts/CascadiaMono-Regular.ttf
Executable file
BIN
res/fonts/CascadiaMono-Regular.ttf
Executable file
Binary file not shown.
Reference in New Issue
Block a user