diff --git a/glyphs/glyphs.go b/glyphs/glyphs.go index 076e798..d9fe12a 100755 --- a/glyphs/glyphs.go +++ b/glyphs/glyphs.go @@ -11,6 +11,7 @@ import ( "github.com/bloeys/nmage/buffers" "github.com/bloeys/nmage/materials" "github.com/bloeys/nmage/meshes" + "github.com/bloeys/nterm/assert" "github.com/bloeys/nterm/consts" "github.com/go-gl/gl/v4.1-core/gl" "github.com/golang/freetype/truetype" @@ -27,6 +28,19 @@ var ( RuneInfos map[rune]RuneInfo ) +type GlyphRendOpt uint64 + +const ( + GlyphRendOpt_None GlyphRendOpt = 0 + GlyphRendOpt_BgColor GlyphRendOpt = 1 << (iota - 1) + GlyphRendOpt_Underline + GlyphRendOpt_COUNT GlyphRendOpt = iota +) + +type GlyphRendOptValues struct { + BgColor *gglm.Vec4 +} + type GlyphRend struct { Atlas *FontAtlas AtlasTex *assets.Texture @@ -37,18 +51,35 @@ type GlyphRend struct { TextRunsBuf []TextRun GlyphCount uint32 - //NOTE: Because of the sad realities (bugs?) of CGO, passing an array in a struct - //to C explodes (Go pointer to Go pointer error) even though passing the same array - //allocated inside the function is fine (Go potentially can't detect what's happening properly). - // //Luckily slices still work, so for now we will use our slice as an array (no appending) GlyphVBO []float32 - // GlyphVBO [floatsPerGlyph * maxGlyphsPerBatch]float32 ScreenWidth int32 ScreenHeight int32 SpacesPerTab uint + + Opts GlyphRendOpt + OptValues GlyphRendOptValues +} + +func (gr *GlyphRend) SetOpts(opts ...GlyphRendOpt) { + + for _, v := range opts { + assert.T(v <= (1<<(GlyphRendOpt_COUNT-2)), "Invalid opts of value %d", opts) + + if v == GlyphRendOpt_None { + gr.Opts = GlyphRendOpt_None + } else { + gr.Opts |= v + } + } + + gl.ProgramUniform1ui(gr.GlyphMat.ShaderProg.ID, gr.GlyphMat.GetUnifLoc("opts1"), uint32(gr.Opts)) +} + +func (gr *GlyphRend) HasOpt(opt GlyphRendOpt) bool { + return gr.Opts&opt != 0 } //DrawTextOpenGLAbs prepares text that will be drawn on the next GlyphRend.Draw call. @@ -273,6 +304,18 @@ func (gr *GlyphRend) DrawTextOpenGLAbsRectWithStartPos(text []rune, startPos, re // @Debug var PrintPositions bool +func (gr *GlyphRend) GridSize() (w, h int64) { + w = int64(gr.ScreenWidth) / int64(gr.Atlas.SpaceAdvance) + h = int64(gr.ScreenHeight) / int64(gr.Atlas.LineHeight) + return w, h +} + +func (gr *GlyphRend) ScreenPosToGridPos(x, y float32) (gridX, gridY float32) { + gridX = floorF32(x / gr.Atlas.SpaceAdvance) + gridY = floorF32(y / gr.Atlas.LineHeight) + return gridX, gridY +} + func (gr *GlyphRend) drawRune(run *TextRun, i int, prevRune rune, pos *gglm.Vec3, color *gglm.Vec4, lineHeightF32 float32, bufIndex *uint32) { r := run.Runes[i] @@ -317,6 +360,42 @@ func (gr *GlyphRend) drawRune(run *TextRun, i int, prevRune rune, pos *gglm.Vec3 } //Add the glyph information to the vbo + if gr.HasOpt(GlyphRendOpt_BgColor) { + //UV + gr.GlyphVBO[*bufIndex+0] = -1 + gr.GlyphVBO[*bufIndex+1] = -1 + *bufIndex += 2 + + //UVSize + gr.GlyphVBO[*bufIndex+0] = 0 + gr.GlyphVBO[*bufIndex+1] = 0 + *bufIndex += 2 + + //Color + gr.GlyphVBO[*bufIndex+0] = gr.OptValues.BgColor.R() + gr.GlyphVBO[*bufIndex+1] = gr.OptValues.BgColor.G() + gr.GlyphVBO[*bufIndex+2] = gr.OptValues.BgColor.B() + gr.GlyphVBO[*bufIndex+3] = gr.OptValues.BgColor.A() + *bufIndex += 4 + + //Model Pos + gr.GlyphVBO[*bufIndex+0] = pos.X() + gr.GlyphVBO[*bufIndex+1] = pos.Y() + gr.GlyphVBO[*bufIndex+2] = pos.Z() + *bufIndex += 3 + + //Model Scale + gr.GlyphVBO[*bufIndex+0] = gr.Atlas.SpaceAdvance + gr.GlyphVBO[*bufIndex+1] = lineHeightF32 + *bufIndex += 2 + + gr.GlyphCount++ + if gr.GlyphCount == DefaultGlyphsPerBatch { + gr.Draw() + *bufIndex = 0 + } + } + //UV gr.GlyphVBO[*bufIndex+0] = g.U gr.GlyphVBO[*bufIndex+1] = g.V @@ -598,7 +677,8 @@ func (gr *GlyphRend) updateFontAtlasTexture() error { //Update material gr.GlyphMat.DiffuseTex = gr.AtlasTex.TexID - // gr.GlyphMat.SetUnifVec2("sizeUV", &gr.Atlas.SizeUV) + // gr.GlyphMat.SetUnifFloat32("spaceAdv", gr.Atlas.SpaceAdvance) + // gr.GlyphMat.SetUnifFloat32("lineHeight", gr.Atlas.LineHeight) return nil } @@ -631,6 +711,11 @@ func NewGlyphRend(fontFile string, fontOptions *truetype.Options, screenWidth, s GlyphVBO: make([]float32, floatsPerGlyph*DefaultGlyphsPerBatch), TextRunsBuf: make([]TextRun, 0, 20), SpacesPerTab: 4, + + Opts: GlyphRendOpt_None, + OptValues: GlyphRendOptValues{ + BgColor: gglm.NewVec4(0, 0, 0, 0), + }, } //Create glyph mesh diff --git a/main.go b/main.go index f1ef9bd..4935911 100755 --- a/main.go +++ b/main.go @@ -122,6 +122,7 @@ var ( drawManyLines = false textToShow = "" + // textToShow = "Hello there, friend!" xOff float32 = 0 yOff float32 = 0 @@ -217,6 +218,9 @@ func (p *nterm) Init() { panic("Failed to create atlas from font file. Err: " + err.Error()) } + p.GlyphRend.OptValues.BgColor = gglm.NewVec4(1, 0, 0, 0.25) + // p.GlyphRend.SetOpts(glyphs.GlyphRendOpt_BgColor) + // if consts.Mode_Debug { // glyphs.SaveImgToPNG(p.GlyphRend.Atlas.Img, "./debug-atlas.png") // } @@ -703,7 +707,6 @@ func (p *nterm) DrawCursor() { //Position cursor by placing it at the end of the drawn characters then walking backwards pos := p.lastCmdCharPos.Clone() - p.ScreenPosToGridPos(pos) pos.AddY(p.GlyphRend.Atlas.LineHeight * 0.5) for i := clamp(p.cmdBufLen, 0, int64(len(p.cmdBuf))); i > p.cursorCharIndex; i-- { @@ -726,8 +729,8 @@ func (p *nterm) GridSize() (w, h int64) { } func (p *nterm) ScreenPosToGridPos(screenPos *gglm.Vec3) { - screenPos.SetX(screenPos.X() / p.GlyphRend.Atlas.SpaceAdvance * p.GlyphRend.Atlas.SpaceAdvance) - screenPos.SetY(screenPos.Y() / p.GlyphRend.Atlas.LineHeight * p.GlyphRend.Atlas.LineHeight) + screenPos.SetX(FloorF32(screenPos.X() / p.GlyphRend.Atlas.SpaceAdvance)) + screenPos.SetY(FloorF32(screenPos.Y() / p.GlyphRend.Atlas.LineHeight)) } func (p *nterm) DebugUpdate() { diff --git a/res/shaders/glyph.glsl b/res/shaders/glyph.glsl index 448eca9..0b1b8a6 100755 --- a/res/shaders/glyph.glsl +++ b/res/shaders/glyph.glsl @@ -13,6 +13,7 @@ layout(location=5) in vec2 aModelScale; out vec2 v2fUV0; out vec4 v2fColor; +out vec3 v2fModelPos; out vec3 v2fFragPos; uniform mat4 projViewMat; @@ -26,8 +27,9 @@ void main() aModelPos.x, aModelPos.y, aModelPos.z, 1.0 ); - v2fUV0 = aUV0 + aVertPos.xy * aUVSize; v2fColor = aVertColor; + v2fModelPos = aModelPos; + v2fUV0 = aUV0 + aVertPos.xy * aUVSize; gl_Position = projViewMat * modelMat * vec4(aVertPos, 1.0); } @@ -37,22 +39,64 @@ void main() in vec2 v2fUV0; in vec4 v2fColor; +in vec3 v2fModelPos; +in vec4 gl_FragCoord; out vec4 fragColor; uniform sampler2D diffTex; -uniform int drawBounds; +uniform int drawGlyphBounds; + +uniform uint opts1; +// uniform float spaceAdv = 1; +// uniform float lineHeight = 1; + +const uint opts1_bgColorMask = 1<<0; +const uint opts1_underlineMask = 1<<1; + +bool hasOpts(uint mask) +{ + return (opts1&mask) != 0; +} + +// vec2 ScreenPosToGridPos(vec2 pos) +// { +// return vec2(floor(pos.x / spaceAdv), floor(pos.y / lineHeight)); +// } + +// vec4 GridPosToCellStartEnd(vec2 gridPos) +// { +// vec4 startEnd; +// startEnd.x = gridPos.x * spaceAdv; +// startEnd.y = gridPos.y * lineHeight; +// startEnd.z = startEnd.x + spaceAdv; +// startEnd.w = startEnd.y + lineHeight; +// return startEnd; +// } void main() { + if (hasOpts(opts1_bgColorMask) && v2fUV0 == vec2(-1,-1)) + { + // vec2 gridPos = ScreenPosToGridPos(v2fModelPos.xy); + // vec4 startEnd = GridPosToCellStartEnd(gridPos); + //if (gl_FragCoord.x > startEnd.x && gl_FragCoord.x < startEnd.z) + { + fragColor = v2fColor; + } + + return; + } + vec4 texColor = texelFetch(diffTex, ivec2(v2fUV0), 0); - // This part highlights the full region of the char - if (texColor.r == 0 && drawBounds != 0) + if (texColor.r == 0) { - fragColor = vec4(0,1,0,0.25); + if (drawGlyphBounds != 0) + { + fragColor = vec4(0,1,0,0.25); + } + return; } - else - { - fragColor = vec4(v2fColor.rgb, texColor.r*v2fColor.a); - } -} + + fragColor = vec4(v2fColor.rgb, texColor.r*v2fColor.a); +}