mirror of
https://github.com/bloeys/nterm.git
synced 2025-12-29 06:28:20 +00:00
Respect bearing,scaling and other metrics
This commit is contained in:
@ -1,11 +1,13 @@
|
||||
package glyphs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"image"
|
||||
"image/draw"
|
||||
"image/png"
|
||||
"math"
|
||||
"os"
|
||||
"unicode"
|
||||
|
||||
"github.com/bloeys/nterm/assert"
|
||||
"github.com/golang/freetype/truetype"
|
||||
@ -29,8 +31,14 @@ type FontAtlasGlyph struct {
|
||||
Ascent float32
|
||||
Descent float32
|
||||
Advance float32
|
||||
BearingX float32
|
||||
Width float32
|
||||
}
|
||||
|
||||
//NewFontAtlasFromFile reads a TTF or TTC file and produces a font texture atlas containing
|
||||
//all its characters using the specified options.
|
||||
//
|
||||
//Only monospaced fonts are supported
|
||||
func NewFontAtlasFromFile(fontFile string, fontOptions *truetype.Options) (*FontAtlas, error) {
|
||||
|
||||
fBytes, err := os.ReadFile(fontFile)
|
||||
@ -44,25 +52,28 @@ func NewFontAtlasFromFile(fontFile string, fontOptions *truetype.Options) (*Font
|
||||
}
|
||||
|
||||
face := truetype.NewFace(f, fontOptions)
|
||||
atlas := NewFontAtlasFromFont(f, face, uint(fontOptions.Size))
|
||||
return atlas, nil
|
||||
return NewFontAtlasFromFont(f, face, uint(fontOptions.Size))
|
||||
}
|
||||
|
||||
func NewFontAtlasFromFont(f *truetype.Font, face font.Face, pointSize uint) *FontAtlas {
|
||||
//NewFontAtlasFromFile uses the passed font to produce a font texture atlas containing
|
||||
//all its characters using the specified options.
|
||||
//
|
||||
//Only monospaced fonts are supported
|
||||
func NewFontAtlasFromFont(f *truetype.Font, face font.Face, pointSize uint) (*FontAtlas, error) {
|
||||
|
||||
const maxAtlasSize = 8192
|
||||
|
||||
glyphs := getGlyphsFromRanges(getGlyphRanges(f))
|
||||
|
||||
glyphs := getGlyphsFromRuneRanges(getGlyphRangesFromFont(f))
|
||||
assert.T(len(glyphs) > 0, "no glyphs")
|
||||
|
||||
//Choose atlas size
|
||||
atlasSizeX := 512
|
||||
atlasSizeY := 512
|
||||
|
||||
_, charWidthFixed, _ := face.GlyphBounds(glyphs[0])
|
||||
charWidth := charWidthFixed.Floor()
|
||||
lineHeight := face.Metrics().Height.Floor()
|
||||
charWidthFixed, _ := face.GlyphAdvance('L')
|
||||
charWidth := charWidthFixed.Ceil()
|
||||
|
||||
lineHeight := face.Metrics().Height.Ceil()
|
||||
|
||||
maxLinesInAtlas := atlasSizeY/lineHeight - 1
|
||||
charsPerLine := atlasSizeX / charWidth
|
||||
@ -78,7 +89,10 @@ func NewFontAtlasFromFont(f *truetype.Font, face font.Face, pointSize uint) *Fon
|
||||
charsPerLine = atlasSizeX / charWidth
|
||||
linesNeeded = int(math.Ceil(float64(len(glyphs)) / float64(charsPerLine)))
|
||||
}
|
||||
assert.T(atlasSizeX <= maxAtlasSize, "Atlas size went beyond maximum")
|
||||
|
||||
if atlasSizeX > maxAtlasSize {
|
||||
return nil, errors.New("atlas size went beyond the maximum of 8192*8192")
|
||||
}
|
||||
|
||||
//Create atlas
|
||||
atlas := &FontAtlas{
|
||||
@ -102,28 +116,33 @@ func NewFontAtlasFromFont(f *truetype.Font, face font.Face, pointSize uint) *Fon
|
||||
atlasSizeYF32 := float32(atlasSizeY)
|
||||
|
||||
charsOnLine := 0
|
||||
lineDx := fixed.P(0, lineHeight)
|
||||
lineHeightFixed := fixed.I(lineHeight)
|
||||
drawer.Dot = fixed.P(0, lineHeight)
|
||||
for _, g := range glyphs {
|
||||
|
||||
gBounds, gAdvanceFixed, _ := face.GlyphBounds(g)
|
||||
advanceCeilF32 := float32(gAdvanceFixed.Ceil())
|
||||
|
||||
descent := gBounds.Max.Y
|
||||
advanceRoundedF32 := float32(gAdvanceFixed.Floor())
|
||||
ascent := -gBounds.Min.Y
|
||||
ascent := absFixedI26_6(gBounds.Min.Y)
|
||||
descent := absFixedI26_6(gBounds.Max.Y)
|
||||
bearingX := absFixedI26_6(gBounds.Min.X)
|
||||
|
||||
heightRounded := (ascent + descent).Floor()
|
||||
glyphWidth := float32((absFixedI26_6(gBounds.Max.X - gBounds.Min.X)).Ceil())
|
||||
heightRounded := (ascent + descent).Ceil()
|
||||
|
||||
atlas.Glyphs[g] = FontAtlasGlyph{
|
||||
U: float32(drawer.Dot.X.Floor()) / atlasSizeXF32,
|
||||
V: (atlasSizeYF32 - float32((drawer.Dot.Y + descent).Floor())) / atlasSizeYF32,
|
||||
U: float32((drawer.Dot.X + bearingX).Floor()) / atlasSizeXF32,
|
||||
V: (atlasSizeYF32 - float32((drawer.Dot.Y + descent).Ceil())) / atlasSizeYF32,
|
||||
|
||||
SizeU: advanceRoundedF32 / atlasSizeXF32,
|
||||
SizeU: glyphWidth / atlasSizeXF32,
|
||||
SizeV: float32(heightRounded) / atlasSizeYF32,
|
||||
|
||||
Ascent: float32(ascent.Floor()),
|
||||
Descent: float32(descent.Floor()),
|
||||
Advance: float32(advanceRoundedF32),
|
||||
Ascent: float32(ascent.Ceil()),
|
||||
Descent: float32(descent.Ceil()),
|
||||
Advance: float32(advanceCeilF32),
|
||||
|
||||
BearingX: float32(bearingX.Ceil()),
|
||||
Width: glyphWidth,
|
||||
}
|
||||
drawer.DrawString(string(g))
|
||||
|
||||
@ -132,11 +151,11 @@ func NewFontAtlasFromFont(f *truetype.Font, face font.Face, pointSize uint) *Fon
|
||||
|
||||
charsOnLine = 0
|
||||
drawer.Dot.X = 0
|
||||
drawer.Dot = drawer.Dot.Add(lineDx)
|
||||
drawer.Dot.Y += lineHeightFixed
|
||||
}
|
||||
}
|
||||
|
||||
return atlas
|
||||
return atlas, nil
|
||||
}
|
||||
|
||||
func SaveImgToPNG(img image.Image, file string) error {
|
||||
@ -154,3 +173,60 @@ func SaveImgToPNG(img image.Image, file string) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//getGlyphRangesFromFont returns a list of ranges, each range is: [i][0]<=range<[i][1]
|
||||
func getGlyphRangesFromFont(f *truetype.Font) (ret [][2]rune) {
|
||||
|
||||
isRuneInPrivateUseArea := func(r rune) bool {
|
||||
return 0xe000 <= r && r <= 0xf8ff ||
|
||||
0xf0000 <= r && r <= 0xffffd ||
|
||||
0x100000 <= r && r <= 0x10fffd
|
||||
}
|
||||
|
||||
rr := [2]rune{-1, -1}
|
||||
for r := rune(0); r <= unicode.MaxRune; r++ {
|
||||
if isRuneInPrivateUseArea(r) {
|
||||
continue
|
||||
}
|
||||
if f.Index(r) == 0 {
|
||||
continue
|
||||
}
|
||||
if rr[1] == r {
|
||||
rr[1] = r + 1
|
||||
continue
|
||||
}
|
||||
if rr[0] != -1 {
|
||||
ret = append(ret, rr)
|
||||
}
|
||||
rr = [2]rune{r, r + 1}
|
||||
}
|
||||
if rr[0] != -1 {
|
||||
ret = append(ret, rr)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
//getGlyphsFromRuneRanges takes ranges of runes and produces an array of all the runes in these ranges
|
||||
func getGlyphsFromRuneRanges(ranges [][2]rune) []rune {
|
||||
|
||||
out := make([]rune, 0)
|
||||
for _, rr := range ranges {
|
||||
|
||||
temp := make([]rune, 0, rr[1]-rr[0])
|
||||
for r := rr[0]; r < rr[1]; r++ {
|
||||
temp = append(temp, r)
|
||||
}
|
||||
|
||||
out = append(out, temp...)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func absFixedI26_6(x fixed.Int26_6) fixed.Int26_6 {
|
||||
if x < 0 {
|
||||
return -x
|
||||
}
|
||||
|
||||
return x
|
||||
}
|
||||
|
||||
131
glyphs/glyphs.go
131
glyphs/glyphs.go
@ -2,8 +2,8 @@ package glyphs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
"os"
|
||||
"unicode"
|
||||
|
||||
"github.com/bloeys/gglm/gglm"
|
||||
"github.com/bloeys/nmage/assets"
|
||||
@ -29,67 +29,24 @@ type GlyphRend struct {
|
||||
ScreenHeight int32
|
||||
}
|
||||
|
||||
//getGlyphRanges returns a list of ranges, each range is: [i][0]<=range<[i][1]
|
||||
func getGlyphRanges(f *truetype.Font) (ret [][2]rune) {
|
||||
rr := [2]rune{-1, -1}
|
||||
for r := rune(0); r <= unicode.MaxRune; r++ {
|
||||
if privateUseArea(r) {
|
||||
continue
|
||||
}
|
||||
if f.Index(r) == 0 {
|
||||
continue
|
||||
}
|
||||
if rr[1] == r {
|
||||
rr[1] = r + 1
|
||||
continue
|
||||
}
|
||||
if rr[0] != -1 {
|
||||
ret = append(ret, rr)
|
||||
}
|
||||
rr = [2]rune{r, r + 1}
|
||||
}
|
||||
if rr[0] != -1 {
|
||||
ret = append(ret, rr)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func privateUseArea(r rune) bool {
|
||||
return 0xe000 <= r && r <= 0xf8ff ||
|
||||
0xf0000 <= r && r <= 0xffffd ||
|
||||
0x100000 <= r && r <= 0x10fffd
|
||||
}
|
||||
|
||||
//getGlyphsFromRanges takes ranges of runes and produces an array of all the runes in these ranges
|
||||
func getGlyphsFromRanges(ranges [][2]rune) []rune {
|
||||
|
||||
out := make([]rune, 0)
|
||||
for _, rr := range ranges {
|
||||
|
||||
temp := make([]rune, 0, rr[1]-rr[0])
|
||||
for r := rr[0]; r < rr[1]; r++ {
|
||||
temp = append(temp, r)
|
||||
}
|
||||
|
||||
out = append(out, temp...)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
//DrawTextOpenGL prepares text that will be drawn on the next GlyphRend.Draw call.
|
||||
//DrawTextOpenGLAbs prepares text that will be drawn on the next GlyphRend.Draw call.
|
||||
//screenPos is in the range [0,1], where (0,0) is the bottom left.
|
||||
//Color is RGBA in the range [0,1].
|
||||
func (gr *GlyphRend) DrawTextOpenGL(text string, screenPos *gglm.Vec3, color *gglm.Vec4) {
|
||||
func (gr *GlyphRend) DrawTextOpenGL01(text string, screenPos *gglm.Vec3, color *gglm.Vec4) {
|
||||
screenPos.Set(screenPos.X()*float32(gr.ScreenWidth), screenPos.Y()*float32(gr.ScreenHeight), screenPos.Z())
|
||||
gr.DrawTextOpenGLAbs(text, screenPos, color)
|
||||
}
|
||||
|
||||
screenWidthF32 := float32(gr.ScreenWidth)
|
||||
screenHeightF32 := float32(gr.ScreenHeight)
|
||||
screenPos.Set(screenPos.X()*screenWidthF32, screenPos.Y()*screenHeightF32, screenPos.Z())
|
||||
//DrawTextOpenGLAbs prepares text that will be drawn on the next GlyphRend.Draw call.
|
||||
//screenPos is in the range ([0,ScreenWidth],[0,ScreenHeight]).
|
||||
//Color is RGBA in the range [0,1].
|
||||
func (gr *GlyphRend) DrawTextOpenGLAbs(text string, screenPos *gglm.Vec3, color *gglm.Vec4) {
|
||||
|
||||
//Prepass to pre-allocate the buffer
|
||||
rs := []rune(text)
|
||||
const floatsPerGlyph = 18
|
||||
|
||||
// startPos := screenPos.Clone()
|
||||
pos := screenPos.Clone()
|
||||
instancedData := make([]float32, 0, len(rs)*floatsPerGlyph) //This a larger approximation than needed because we don't count spaces etc
|
||||
for i := 0; i < len(rs); i++ {
|
||||
@ -100,11 +57,15 @@ func (gr *GlyphRend) DrawTextOpenGL(text string, screenPos *gglm.Vec3, color *gg
|
||||
screenPos.SetY(screenPos.Y() - float32(gr.Atlas.LineHeight))
|
||||
pos = screenPos.Clone()
|
||||
continue
|
||||
} else if r == ' ' {
|
||||
pos.SetX(pos.X() + g.Advance)
|
||||
continue
|
||||
}
|
||||
gr.GlyphCount++
|
||||
|
||||
height := float32(g.Ascent + g.Descent)
|
||||
scale := gglm.NewVec3(g.Advance, height, 1)
|
||||
glyphHeight := float32(g.Ascent + g.Descent)
|
||||
scale := gglm.NewVec3(g.Width, glyphHeight, 1)
|
||||
// scale := gglm.NewVec3(g.Advance, glyphHeight, 1)
|
||||
|
||||
//See: https://developer.apple.com/library/archive/documentation/TextFonts/Conceptual/CocoaTextArchitecture/Art/glyph_metrics_2x.png
|
||||
//Quads are drawn from the center and so that's our baseline. But chars shouldn't be centered, they should follow ascent/decent/advance.
|
||||
@ -113,8 +74,8 @@ func (gr *GlyphRend) DrawTextOpenGL(text string, screenPos *gglm.Vec3, color *gg
|
||||
//
|
||||
//Horizontally the character should be drawn from the left edge not the center, so we just move it forward by advance/2
|
||||
drawPos := *pos
|
||||
drawPos.SetX(drawPos.X() + g.Advance*0.5)
|
||||
drawPos.SetY(drawPos.Y() + height*0.5 - g.Descent)
|
||||
drawPos.SetX(drawPos.X() + g.BearingX)
|
||||
drawPos.SetY(drawPos.Y() - g.Descent)
|
||||
|
||||
instancedData = append(instancedData, []float32{
|
||||
g.U, g.V,
|
||||
@ -123,13 +84,32 @@ func (gr *GlyphRend) DrawTextOpenGL(text string, screenPos *gglm.Vec3, color *gg
|
||||
g.U + g.SizeU, g.V + g.SizeV,
|
||||
|
||||
color.R(), color.G(), color.B(), color.A(), //Color
|
||||
drawPos.X(), drawPos.Y(), drawPos.Z(), //Model pos
|
||||
roundF32(drawPos.X()), roundF32(drawPos.Y()), drawPos.Z(), //Model pos
|
||||
scale.X(), scale.Y(), scale.Z(), //Model scale
|
||||
}...)
|
||||
|
||||
pos.SetX(pos.X() + g.Advance)
|
||||
}
|
||||
|
||||
//Draw baselines
|
||||
// g := gr.Atlas.Glyphs['-']
|
||||
// lineData := []float32{
|
||||
// g.U, g.V,
|
||||
// g.U + g.SizeU, g.V,
|
||||
// g.U, g.V + g.SizeV,
|
||||
// g.U + g.SizeU, g.V + g.SizeV,
|
||||
|
||||
// 1, 0, 0, 1, //Color
|
||||
// 0, startPos.Y(), 1, //Model pos
|
||||
// float32(gr.ScreenWidth), 5, 1, //Model scale
|
||||
// }
|
||||
|
||||
// instancedData = append(instancedData, lineData...)
|
||||
// lineData[13] -= float32(gr.Atlas.LineHeight)
|
||||
// instancedData = append(instancedData, lineData...)
|
||||
// gr.GlyphCount++
|
||||
// gr.GlyphCount++
|
||||
|
||||
gr.GlyphVBO = append(gr.GlyphVBO, instancedData...)
|
||||
}
|
||||
|
||||
@ -149,10 +129,19 @@ func (gr *GlyphRend) Draw() {
|
||||
gr.GlyphVBO = []float32{}
|
||||
}
|
||||
|
||||
func (gr *GlyphRend) SetFace(fontOptions *truetype.Options) {
|
||||
//SetFace updates the underlying font atlas used by the glyph renderer.
|
||||
//The current atlas is unchanged if there is an error
|
||||
func (gr *GlyphRend) SetFace(fontOptions *truetype.Options) error {
|
||||
|
||||
face := truetype.NewFace(gr.Atlas.Font, fontOptions)
|
||||
gr.Atlas = NewFontAtlasFromFont(gr.Atlas.Font, face, uint(fontOptions.Size))
|
||||
newAtlas, err := NewFontAtlasFromFont(gr.Atlas.Font, face, uint(fontOptions.Size))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gr.Atlas = newAtlas
|
||||
gr.updateFontAtlasTexture("temp-atlas")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gr *GlyphRend) SetFontFromFile(fontFile string, fontOptions *truetype.Options) error {
|
||||
@ -210,9 +199,9 @@ func (gr *GlyphRend) SetScreenSize(screenWidth, screenHeight int32) {
|
||||
gr.ScreenHeight = screenHeight
|
||||
|
||||
//The projection matrix fits the screen size. This is needed so we can size and position characters correctly.
|
||||
projMtx := gglm.Ortho(0, float32(screenWidth), float32(screenHeight), 0, 0.1, 10)
|
||||
viewMtx := gglm.LookAt(gglm.NewVec3(0, 0, -1), gglm.NewVec3(0, 0, 0), gglm.NewVec3(0, 1, 0))
|
||||
projViewMtx := projMtx.Clone().Mul(viewMtx)
|
||||
projMtx := gglm.Ortho(0, float32(screenWidth), float32(screenHeight), 0, 0.1, 20)
|
||||
viewMtx := gglm.LookAt(gglm.NewVec3(0, 0, -10), gglm.NewVec3(0, 0, 0), gglm.NewVec3(0, 1, 0))
|
||||
projViewMtx := projMtx.Mul(viewMtx)
|
||||
|
||||
gr.GlyphMat.DiffuseTex = gr.AtlasTex.TexID
|
||||
gr.GlyphMat.SetUnifMat4("projViewMat", &projViewMtx.Mat4)
|
||||
@ -246,11 +235,12 @@ func NewGlyphRend(fontFile string, fontOptions *truetype.Options, screenWidth, s
|
||||
),
|
||||
}
|
||||
|
||||
//The quad must be anchored at the bottom-left, not it's center (i.e. bottom-left vertex must be at 0,0)
|
||||
gr.GlyphMesh.Buf.SetData([]float32{
|
||||
-0.5, -0.5, 0,
|
||||
0.5, -0.5, 0,
|
||||
-0.5, 0.5, 0,
|
||||
0.5, 0.5, 0,
|
||||
0, 0, 0,
|
||||
1, 0, 0,
|
||||
0, 1, 0,
|
||||
1, 1, 0,
|
||||
})
|
||||
|
||||
gr.GlyphMesh.Buf.SetIndexBufData([]uint32{
|
||||
@ -329,5 +319,10 @@ func NewGlyphRend(fontFile string, fontOptions *truetype.Options, screenWidth, s
|
||||
gr.InstancedBuf.UnBind()
|
||||
|
||||
gr.SetScreenSize(screenWidth, screenHeight)
|
||||
// fmt.Printf("lineHeight=%d, glyphInfo=%+v\n", gr.Atlas.LineHeight, gr.Atlas.Glyphs['A'])
|
||||
return gr, nil
|
||||
}
|
||||
|
||||
func roundF32(x float32) float32 {
|
||||
return float32(math.Round(float64(x)))
|
||||
}
|
||||
|
||||
90
main.go
90
main.go
@ -1,12 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/bloeys/gglm/gglm"
|
||||
"github.com/bloeys/nmage/engine"
|
||||
"github.com/bloeys/nmage/input"
|
||||
"github.com/bloeys/nmage/materials"
|
||||
"github.com/bloeys/nmage/meshes"
|
||||
"github.com/bloeys/nmage/renderer/rend3dgl"
|
||||
nmageimgui "github.com/bloeys/nmage/ui/imgui"
|
||||
"github.com/bloeys/nterm/glyphs"
|
||||
"github.com/go-gl/gl/v4.1-core/gl"
|
||||
"github.com/golang/freetype/truetype"
|
||||
"github.com/veandco/go-sdl2/sdl"
|
||||
)
|
||||
@ -32,6 +37,9 @@ type program struct {
|
||||
// * Allow texture loading without cache
|
||||
// * Reduce/remove Game interface
|
||||
|
||||
const subPixelX = 64
|
||||
const subPixelY = 64
|
||||
|
||||
func main() {
|
||||
|
||||
err := engine.Init()
|
||||
@ -53,7 +61,7 @@ func main() {
|
||||
rend: rend,
|
||||
imguiInfo: nmageimgui.NewImGUI(),
|
||||
|
||||
FontSize: 32,
|
||||
FontSize: 80,
|
||||
}
|
||||
|
||||
p.win.EventCallbacks = append(p.win.EventCallbacks, func(e sdl.Event) {
|
||||
@ -65,6 +73,10 @@ func main() {
|
||||
}
|
||||
})
|
||||
|
||||
//Don't flash white
|
||||
gl.ClearColor(0, 0, 0, 0)
|
||||
p.win.SDLWin.GLSwap()
|
||||
|
||||
engine.Run(p)
|
||||
}
|
||||
|
||||
@ -74,17 +86,31 @@ func (p *program) Init() {
|
||||
if err != nil {
|
||||
panic("Failed to get display DPI. Err: " + err.Error())
|
||||
}
|
||||
println("DPI:", dpi)
|
||||
fmt.Println("DPI:", dpi)
|
||||
|
||||
w, h := p.win.SDLWin.GetSize()
|
||||
p.GlyphRend, err = glyphs.NewGlyphRend("./res/fonts/Consolas.ttf", &truetype.Options{Size: float64(p.FontSize), DPI: p.Dpi}, w, h)
|
||||
p.GlyphRend, err = glyphs.NewGlyphRend("./res/fonts/Consolas.ttf", &truetype.Options{Size: float64(p.FontSize), DPI: p.Dpi, SubPixelsX: subPixelX, SubPixelsY: subPixelY}, w, h)
|
||||
if err != nil {
|
||||
panic("Failed to create atlas from font file. Err: " + err.Error())
|
||||
}
|
||||
|
||||
glyphs.SaveImgToPNG(p.GlyphRend.Atlas.Img, "./debug-atlas.png")
|
||||
}
|
||||
|
||||
var gridMesh *meshes.Mesh
|
||||
var gridMat *materials.Material
|
||||
|
||||
func (p *program) Start() {
|
||||
|
||||
var err error
|
||||
gridMesh, err = meshes.NewMesh("grid", "./res/models/quad.obj", 0)
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
|
||||
gridMat = materials.NewMaterial("grid", "./res/shaders/grid")
|
||||
gridMat.SetAttribute(gridMesh.Buf)
|
||||
p.handleWindowResize()
|
||||
}
|
||||
|
||||
func (p *program) FrameStart() {
|
||||
@ -100,6 +126,7 @@ func (p *program) Update() {
|
||||
p.handleWindowResize()
|
||||
}
|
||||
|
||||
oldFont := p.FontSize
|
||||
fontSizeChanged := false
|
||||
if input.KeyClicked(sdl.K_KP_PLUS) {
|
||||
p.FontSize += 2
|
||||
@ -110,16 +137,62 @@ func (p *program) Update() {
|
||||
}
|
||||
|
||||
if fontSizeChanged {
|
||||
p.GlyphRend.SetFace(&truetype.Options{Size: float64(p.FontSize), DPI: p.Dpi})
|
||||
|
||||
err := p.GlyphRend.SetFace(&truetype.Options{Size: float64(p.FontSize), DPI: p.Dpi, SubPixelsX: subPixelX, SubPixelsY: subPixelY})
|
||||
if err != nil {
|
||||
p.FontSize = oldFont
|
||||
println("Failed to update font face. Err: " + err.Error())
|
||||
} else {
|
||||
glyphs.SaveImgToPNG(p.GlyphRend.Atlas.Img, "./debug-atlas.png")
|
||||
println("New font size:", p.FontSize)
|
||||
}
|
||||
}
|
||||
|
||||
var speed float32 = 1
|
||||
if input.KeyDown(sdl.K_RIGHT) {
|
||||
xOff += speed
|
||||
} else if input.KeyDown(sdl.K_LEFT) {
|
||||
xOff -= speed
|
||||
}
|
||||
|
||||
if input.KeyDown(sdl.K_UP) {
|
||||
yOff += speed
|
||||
} else if input.KeyDown(sdl.K_DOWN) {
|
||||
yOff -= speed
|
||||
}
|
||||
}
|
||||
|
||||
var xOff float32 = 0
|
||||
var yOff float32 = 0
|
||||
|
||||
func (p *program) Render() {
|
||||
|
||||
defer p.GlyphRend.Draw()
|
||||
|
||||
textColor := gglm.NewVec4(1, 1, 1, 1)
|
||||
p.GlyphRend.DrawTextOpenGL("Hello there, friend.", gglm.NewVec3(0, 0.9, 0), textColor)
|
||||
// p.GlyphRend.DrawTextOpenGL("y", gglm.NewVec3(0+xOff, 0+yOff, 0), textColor)
|
||||
// p.GlyphRend.DrawTextOpenGL("A\np-+_; This is", gglm.NewVec3(0.3+xOff, 0.5+yOff, 0), textColor)
|
||||
// p.GlyphRend.DrawTextOpenGLAbs("Hello there, friend.\nABCDEFGHIJKLMNOPQRSTUVWXYZ", gglm.NewVec3(0, 0, 0), textColor)
|
||||
p.GlyphRend.DrawTextOpenGLAbs(" Hello there, friend|.\n ABCDEFGHIJKLMNOPQRSTUVWXYZ", gglm.NewVec3(xOff, float32(p.GlyphRend.Atlas.LineHeight)*2+yOff, 0), textColor)
|
||||
|
||||
// p.drawGrid()
|
||||
}
|
||||
|
||||
func (p *program) drawGrid() {
|
||||
|
||||
sizeX := float32(p.GlyphRend.ScreenWidth)
|
||||
sizeY := float32(p.GlyphRend.ScreenHeight)
|
||||
|
||||
//columns
|
||||
adv := p.GlyphRend.Atlas.Glyphs['A'].Advance
|
||||
for i := int32(0); i < p.GlyphRend.ScreenWidth; i += int32(adv) {
|
||||
p.rend.Draw(gridMesh, gglm.NewTrMatId().Translate(gglm.NewVec3(float32(i)+0.5, sizeY/2, 0)).Scale(gglm.NewVec3(1, sizeY, 1)), gridMat)
|
||||
}
|
||||
|
||||
//rows
|
||||
for i := int32(0); i < p.GlyphRend.ScreenHeight; i += int32(p.GlyphRend.Atlas.LineHeight) {
|
||||
p.rend.Draw(gridMesh, gglm.NewTrMatId().Translate(gglm.NewVec3(sizeX/2, float32(i), 0)).Scale(gglm.NewVec3(sizeX, 1, 1)), gridMat)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *program) FrameEnd() {
|
||||
@ -142,9 +215,10 @@ func (p *program) Deinit() {
|
||||
}
|
||||
|
||||
func (p *program) handleWindowResize() {
|
||||
|
||||
println("Old size:", p.GlyphRend.ScreenWidth, ",", p.GlyphRend.ScreenHeight)
|
||||
w, h := p.win.SDLWin.GetSize()
|
||||
p.GlyphRend.SetScreenSize(w, h)
|
||||
println("New size:", w, ",", h, "\n")
|
||||
|
||||
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))
|
||||
gridMat.SetUnifMat4("projViewMat", &projMtx.Mul(viewMtx).Mat4)
|
||||
}
|
||||
|
||||
@ -11,5 +11,12 @@ uniform sampler2D diffTex;
|
||||
void main()
|
||||
{
|
||||
vec4 texColor = texture(diffTex, vertUV0);
|
||||
fragColor = vec4(vertColor.rgb, texColor.r*texColor.a);
|
||||
// if (texColor.r == 0)
|
||||
// {
|
||||
// fragColor = vec4(0,1,0,0.25);
|
||||
// }
|
||||
// else
|
||||
{
|
||||
fragColor = vec4(vertColor.rgb, texColor.r);
|
||||
}
|
||||
}
|
||||
|
||||
12
res/shaders/grid.frag.glsl
Executable file
12
res/shaders/grid.frag.glsl
Executable file
@ -0,0 +1,12 @@
|
||||
#version 410
|
||||
|
||||
in vec4 vertColor;
|
||||
in vec2 vertUV0;
|
||||
in vec3 fragPos;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
fragColor = vec4(1, 1, 1, 1);
|
||||
}
|
||||
22
res/shaders/grid.vert.glsl
Executable file
22
res/shaders/grid.vert.glsl
Executable file
@ -0,0 +1,22 @@
|
||||
#version 410
|
||||
|
||||
layout(location=0) in vec3 vertPosIn;
|
||||
layout(location=1) in vec4 vertNormalIn;
|
||||
layout(location=2) in vec2 vertUV0In;
|
||||
layout(location=3) in vec4 vertColorIn;
|
||||
|
||||
out vec2 vertUV0;
|
||||
out vec4 vertColor;
|
||||
out vec3 fragPos;
|
||||
|
||||
uniform mat4 modelMat;
|
||||
uniform mat4 projViewMat;
|
||||
|
||||
void main()
|
||||
{
|
||||
vertUV0 = vertUV0In;
|
||||
vertColor = vertColorIn;
|
||||
fragPos = vec3(modelMat * vec4(vertPosIn, 1.0));
|
||||
|
||||
gl_Position = projViewMat * modelMat * vec4(vertPosIn, 1.0);
|
||||
}
|
||||
Reference in New Issue
Block a user