From 3c1fb484b995a26d3933d201a71d2a7aa580932a Mon Sep 17 00:00:00 2001 From: Marvin Blum Date: Sun, 7 Aug 2016 00:32:16 +0200 Subject: [PATCH] Added texture mapped font rendering. --- ToDo.md | 2 +- demo/text/assets/victor.json | 113 +++++++++++++ demo/text/assets/victor.png | Bin 0 -> 2262 bytes demo/text/text.go | 46 ++++-- game.go | 1 + text.go | 297 +++++++++++++++++------------------ 6 files changed, 290 insertions(+), 169 deletions(-) create mode 100644 demo/text/assets/victor.json create mode 100644 demo/text/assets/victor.png diff --git a/ToDo.md b/ToDo.md index cffe077..ddc5b24 100644 --- a/ToDo.md +++ b/ToDo.md @@ -5,4 +5,4 @@ * limit FPS * fullscreen * simple access to default resources like GetTex() -* text rendering + font loading +* ~~text rendering + font loading~~ diff --git a/demo/text/assets/victor.json b/demo/text/assets/victor.json new file mode 100644 index 0000000..5863fd8 --- /dev/null +++ b/demo/text/assets/victor.json @@ -0,0 +1,113 @@ +[ + {"char":"a", "x": 0, "y": 0, "offset": 0}, + {"char":"b", "x": 1, "y": 0, "offset": 0}, + {"char":"c", "x": 2, "y": 0, "offset": 0}, + {"char":"d", "x": 3, "y": 0, "offset": 0}, + {"char":"e", "x": 4, "y": 0, "offset": 0}, + {"char":"f", "x": 5, "y": 0, "offset": 0}, + {"char":"g", "x": 6, "y": 0, "offset": 0}, + {"char":"h", "x": 7, "y": 0, "offset": 0}, + {"char":"i", "x": 8, "y": 0, "offset": 0}, + {"char":"j", "x": 9, "y": 0, "offset": 0}, + {"char":"k", "x": 10, "y": 0, "offset": 0}, + {"char":"l", "x": 11, "y": 0, "offset": 0}, + {"char":"m", "x": 12, "y": 0, "offset": 0}, + {"char":"n", "x": 13, "y": 0, "offset": 0}, + {"char":"o", "x": 14, "y": 0, "offset": 0}, + {"char":"p", "x": 15, "y": 0, "offset": 0}, + {"char":"q", "x": 16, "y": 0, "offset": 0}, + {"char":"r", "x": 17, "y": 0, "offset": 0}, + {"char":"s", "x": 18, "y": 0, "offset": 0}, + {"char":"t", "x": 19, "y": 0, "offset": 0}, + {"char":"u", "x": 20, "y": 0, "offset": 0}, + {"char":"v", "x": 21, "y": 0, "offset": 0}, + {"char":"w", "x": 22, "y": 0, "offset": 0}, + {"char":"x", "x": 23, "y": 0, "offset": 0}, + {"char":"y", "x": 24, "y": 0, "offset": 0}, + {"char":"z", "x": 25, "y": 0, "offset": 0}, + + {"char":"A", "x": 0, "y": 0, "offset": 0}, + {"char":"B", "x": 1, "y": 0, "offset": 0}, + {"char":"C", "x": 2, "y": 0, "offset": 0}, + {"char":"D", "x": 3, "y": 0, "offset": 0}, + {"char":"E", "x": 4, "y": 0, "offset": 0}, + {"char":"F", "x": 5, "y": 0, "offset": 0}, + {"char":"G", "x": 6, "y": 0, "offset": 0}, + {"char":"H", "x": 7, "y": 0, "offset": 0}, + {"char":"I", "x": 8, "y": 0, "offset": 0}, + {"char":"J", "x": 9, "y": 0, "offset": 0}, + {"char":"K", "x": 10, "y": 0, "offset": 0}, + {"char":"L", "x": 11, "y": 0, "offset": 0}, + {"char":"M", "x": 12, "y": 0, "offset": 0}, + {"char":"N", "x": 13, "y": 0, "offset": 0}, + {"char":"O", "x": 14, "y": 0, "offset": 0}, + {"char":"P", "x": 15, "y": 0, "offset": 0}, + {"char":"Q", "x": 16, "y": 0, "offset": 0}, + {"char":"R", "x": 17, "y": 0, "offset": 0}, + {"char":"S", "x": 18, "y": 0, "offset": 0}, + {"char":"T", "x": 19, "y": 0, "offset": 0}, + {"char":"U", "x": 20, "y": 0, "offset": 0}, + {"char":"V", "x": 21, "y": 0, "offset": 0}, + {"char":"W", "x": 22, "y": 0, "offset": 0}, + {"char":"X", "x": 23, "y": 0, "offset": 0}, + {"char":"Y", "x": 24, "y": 0, "offset": 0}, + {"char":"Z", "x": 25, "y": 0, "offset": 0}, + + {"char":"0", "x": 26, "y": 0, "offset": 0}, + {"char":"1", "x": 27, "y": 0, "offset": 0}, + {"char":"2", "x": 28, "y": 0, "offset": 0}, + {"char":"3", "x": 29, "y": 0, "offset": 0}, + {"char":"4", "x": 30, "y": 0, "offset": 0}, + {"char":"5", "x": 31, "y": 0, "offset": 0}, + {"char":"6", "x": 32, "y": 0, "offset": 0}, + {"char":"7", "x": 33, "y": 0, "offset": 0}, + {"char":"8", "x": 34, "y": 0, "offset": 0}, + {"char":"9", "x": 35, "y": 0, "offset": 0}, + + {"char":"ö", "x": 36, "y": 0, "offset": 0}, + {"char":"ä", "x": 37, "y": 0, "offset": 0}, + {"char":"ü", "x": 38, "y": 0, "offset": 0}, + {"char":"ß", "x": 39, "y": 0, "offset": 0}, + + {"char":"Ö", "x": 36, "y": 0, "offset": 0}, + {"char":"Ä", "x": 37, "y": 0, "offset": 0}, + {"char":"Ü", "x": 38, "y": 0, "offset": 0}, + + {"char":",", "x": 68, "y": 0, "offset": -0.25}, + {"char":";", "x": 69, "y": 0, "offset": 0}, + {"char":".", "x": 70, "y": 0, "offset": 0}, + {"char":":", "x": 71, "y": 0, "offset": 0}, + {"char":"-", "x": 72, "y": 0, "offset": 0}, + {"char":"_", "x": 73, "y": 0, "offset": 0}, + + {"char":"^", "x": 40, "y": 0, "offset": 0}, + {"char":"°", "x": 41, "y": 0, "offset": 0}, + {"char":"!", "x": 42, "y": 0, "offset": 0}, + {"char":"\"", "x": 43, "y": 0, "offset": 0}, + {"char":"§", "x": 44, "y": 0, "offset": 0}, + {"char":"$", "x": 45, "y": 0, "offset": 0}, + {"char":"%", "x": 46, "y": 0, "offset": 0}, + {"char":"&", "x": 47, "y": 0, "offset": 0}, + {"char":"/", "x": 48, "y": 0, "offset": 0}, + {"char":"(", "x": 49, "y": 0, "offset": 0}, + {"char":")", "x": 50, "y": 0, "offset": 0}, + {"char":"=", "x": 51, "y": 0, "offset": 0}, + {"char":"?", "x": 52, "y": 0, "offset": 0}, + {"char":"`", "x": 53, "y": 0, "offset": 0}, + {"char":"²", "x": 54, "y": 0, "offset": 0}, + {"char":"³", "x": 55, "y": 0, "offset": 0}, + {"char":"{", "x": 56, "y": 0, "offset": 0}, + {"char":"[", "x": 57, "y": 0, "offset": 0}, + {"char":"]", "x": 58, "y": 0, "offset": 0}, + {"char":"}", "x": 59, "y": 0, "offset": 0}, + {"char":"\\", "x": 60, "y": 0, "offset": 0}, + {"char":"+", "x": 61, "y": 0, "offset": 0}, + {"char":"*", "x": 62, "y": 0, "offset": 0}, + {"char":"~", "x": 63, "y": 0, "offset": 0}, + {"char":"#", "x": 64, "y": 0, "offset": 0}, + {"char":"<", "x": 65, "y": 0, "offset": 0}, + {"char":">", "x": 66, "y": 0, "offset": 0}, + {"char":"|", "x": 67, "y": 0, "offset": 0}, + {"char":"'", "x": 68, "y": 0, "offset": 0}, + {"char":"'", "x": 69, "y": 0, "offset": 0} +] diff --git a/demo/text/assets/victor.png b/demo/text/assets/victor.png new file mode 100644 index 0000000000000000000000000000000000000000..dc053a8d8cf84aa1595dbccf5849b5756aea9b4e GIT binary patch literal 2262 zcmY*ac{tQ-8y;Cp*(zH(2$fWth@=e72s8GwOo)(W6lw-pf6;<=WDO&kEXAQPN0uQc zsu`m!!#9|z&#_FFiD4$Q{m%5A^Urr**Yn=bbzkrGZ145HZ??Ou!?rEjTObg~HYZ1f zCj=r>A@Nld-Kfj(?yoijIeiVetDf{KWcT`#-KQI-k9SDHX~zESW9jC` zo^7}TKIvxA(tEaa#XGTb8a{@*OC8*dkA^&1S)6iY*PCMnKx5-#*%@tCXZ1do)6ri& z{XgzxDcfFws8m~0Qr=MnzXy`Kj2uBfFE8Ea-ctYW^!F`F{F36n3-ag6f8XCrD8)`t zgyBejZ*MQk%PaL01$buB^VZ$1i|Qen#xLBLnVXEp{N*_Y(F@;kt_b)ZRFw!?Gz|vN z+Ix8s^*uBp)v-xQd?_=qJeNB^KmQuLY!)V#uHRGv1kL-N`(IvKe(a_@#dA&D_Y;Ou zL9?=|on(qNMfll@x1p^Rtkl6vUQ#w=_n*wnYOQY}R(R+YgvN5d?hu65OeZfP4)fl!e*z(JNkc95gVMlNZUjGC$luoyNX!Io_1vNKG(ZuFwF_Zx! zjmoc5-nW<)F_8=oX>w}B6VoglUtvg!`k2&?SUY9Lb_|`7^t+mG;Z75ps&0O2R^g^uKyR@w}*xy zjVQc2GI-AK#TTxzYOFSw7%Cu6Oj!~Aj)EgsrOm(qW+l%9BOsiyon7WM4@Z4u*7faa zTg0C$Y}pghPXlh4zpyj-?K+z$bNa`mCa1Yq-zq`2Ms+-1oH}euhJDlFBCBG;VhJ08 zhDC`v*W_;AI26Wcq*3eY=&xxECl~yFX$@DfYGVwT2y9^}sj@rSyX@Ubpq!cM>5ZkA zFeD4CRz%_LMrUjL zjifwU$J3c1qQV^;kpjG?s6h+<0HT>JU`%%BlDZu(#?m#<6T5}eI-NJ7hus)zB_xPnn~pddtXIr>9_IPO}ayR&zm<1w?8a`sb6TenwixD$p5i0l#TU z{5ty5;^W1N09JOEq2MTiMcA&|HcYhc9)H*v`m-}$G-m^^L`&6k7+C`_=fHQG7*-%= z!)Rk{J>EY_@ncc3^ImMW5ILN=YT%-40w53+Vg4QI(mGfG)sMM0bi&UIEPE*`WPwGC znckvE-m~Kx&oj4QUZNxej);kG{pYP0cAc^)a|N1cRLK?4#QMYD6drnhm3-M1jk2HdxwR!K?q?d$Gy9_JA35!1e z66d}5ek9)oN5ti1Ag7sWi<|dhi*9=IO|-BWS4O{DbWgI$t=H*yrQn?{m)OvRR@5mZZrPb5;*3G#|i<$Oix~Gap**bs$`29GawEu3mag(fDuN#_o==zqKe)Wzg+1BsFuRu9xP{qmh5Gdy%bQa^c zV&L;Qm!qunkQ-z5F6SwFp+VT+c)%VpQkwpG{X(`*k85b?sNYiHfhQek7gqbG2178{ zl~@LbQTgX--{z8lZ5wgaQx<*ZR5JOkYu-9Z$>GA9G0J+d@t~EGeglV8jDe%b;8B4` z;WM5-zjk%v;AwTP^4J88TMr(Az(MOfH$@X>kSX3>E?(&B>EIBtkoek}a5?UDYYR6o4klAvteeEfyX?)wd-C1v1Tn}(hiH7< z&S{d@hd4I@QG>s`N$fzLyI-JND~=im8&LcAd;*x7XQ3QRBR*+JG?yr%wpm>C8jJ1k zKzxj<=;ai6mOndowm$Je$};=|>H@reRGceBU3lJE$0fIIdYSkj6&Vl_5OO&3UOXd} zT&X3c-+AU_iw{W>pIH6u_Z4wd>&O^_A-)#MA&Y#(`eo^kMa+$xGLv)cOZHO0H#9E i41@CjerOU3G7>6Ix?@)yXDdQ}sZRE;h=$Wa8Gi$CgLY*A literal 0 HcmV?d00001 diff --git a/demo/text/text.go b/demo/text/text.go index c5a46fa..3e0769d 100644 --- a/demo/text/text.go +++ b/demo/text/text.go @@ -2,45 +2,63 @@ package main import ( "github.com/DeKugelschieber/go-game" + "github.com/go-gl/gl/v4.5-core/gl" ) const ( - font_path = "src/github.com/DeKugelschieber/go-game/demo/text/assets/font.png" + font_path = "src/github.com/DeKugelschieber/go-game/demo/text/assets/victor.png" + font_json = "src/github.com/DeKugelschieber/go-game/demo/text/assets/victor.json" ) type Game struct{} func (g *Game) Setup() { // load texture - /*_, err := goga.LoadRes(gopher_path) + pngLoader, ok := goga.GetLoaderByExt("png").(*goga.PngLoader) + + if !ok { + panic("Could not get PNG loader") + } + + pngLoader.KeepData = true + pngLoader.Filter = gl.NEAREST + _, err := goga.LoadRes(font_path) if err != nil { panic(err) } - // create sprite - tex, err := goga.GetTex("gopher.png") + pngLoader.KeepData = false + pngLoader.Filter = gl.LINEAR + + // create font + tex, err := goga.GetTex("victor.png") if err != nil { panic(err) } - sprite := goga.NewSprite(tex) - renderer, ok := goga.GetSystemByName("spriteRenderer").(*goga.SpriteRenderer) + font := goga.NewFont(tex, 16) + + if err := font.FromJson(font_json, true); err != nil { + panic(err) + } + + // setup renderer + renderer, ok := goga.GetSystemByName("textRenderer").(*goga.TextRenderer) if !ok { panic("Could not find renderer") } - renderer.Add(sprite.Actor, sprite.Pos2D, sprite.Tex) + renderer.Font = font - culling, ok := goga.GetSystemByName("culling2d").(*goga.Culling2D) - - if !ok { - panic("Could not find culling") - } - - culling.Add(sprite.Actor, sprite.Pos2D)*/ + // create and add text + text := goga.NewText(font, "Hello, World!_") + text.Size = goga.Vec2{16, 16} + text.Pos = goga.Vec2{20, 20} + renderer.Prepare(text) + renderer.Add(text.Actor, text.Pos2D, text.TextComponent) } func (g *Game) Update(delta float64) {} diff --git a/game.go b/game.go index 0448126..ca386c8 100644 --- a/game.go +++ b/game.go @@ -230,6 +230,7 @@ func initGoga(width, height int) { AddSystem(NewModelRenderer(nil, nil, false)) AddSystem(NewCulling2D(0, 0, width, height)) AddSystem(NewKeyframeRenderer(nil, nil)) + AddSystem(NewTextRenderer(nil, nil, nil)) // font must be set outside! } func cleanup() { diff --git a/text.go b/text.go index df5e2eb..0220bc1 100644 --- a/text.go +++ b/text.go @@ -1,6 +1,15 @@ package goga -import () +import ( + "encoding/json" + "github.com/go-gl/gl/v4.5-core/gl" + "io/ioutil" +) + +const ( + char_padding = 2 + text_renderer_name = "textRenderer" +) type character struct { char byte @@ -8,6 +17,11 @@ type character struct { offset float64 } +type jsonChar struct { + Char string + X, Y, Offset float64 +} + // Font represents a texture mapped font. // It can be loaded from JSON together with a texture. type Font struct { @@ -18,11 +32,10 @@ type Font struct { chars []character } -// Text is an actor representing text rendered as texture mapped font. -// Each Text has a position and its own buffers. -type Text struct { - *Actor - *Pos2D +// Renderable text component. +// Use together with Text and create using NewText(). +type TextComponent struct { + Color Vec4 text string bounds Vec2 @@ -30,6 +43,14 @@ type Text struct { vao *VAO } +// Text is an actor representing text rendered as texture mapped font. +// Each Text has a position and its own buffers. +type Text struct { + *Actor + *Pos2D + *TextComponent +} + // The text renderer is a system rendering 2D texture mapped font. // It has a 2D position component, to move all texts at once. type TextRenderer struct { @@ -38,74 +59,64 @@ type TextRenderer struct { Shader *Shader Camera *Camera Font *Font - Color Vec4 // TODO move this to Text? texts []Text } -/* -import ( - "core" - "dp" - "encoding/json" - "geo" - "github.com/go-gl/gl/v4.5-core/gl" - "io/ioutil" - "util" -) - -const ( - char_padding = 2 -) - -// Returns a new renderable text object. -func NewText(font *Font, text string) *Text { - t := &Text{id: core.NextId()} - t.text = text - t.index = dp.NewVBO(gl.ELEMENT_ARRAY_BUFFER) - t.vertex = dp.NewVBO(gl.ARRAY_BUFFER) - t.texCoord = dp.NewVBO(gl.ARRAY_BUFFER) - t.vao = dp.NewVAO() - t.SetText(font, text) - t.Size = geo.Vec2{1, 1} - t.Scale = geo.Vec2{1, 1} - t.Visible = true - - return t -} - -type Character struct { - char byte - min, max, size geo.Vec2 - offset float64 -} - -type Font struct { - Tex *dp.Tex - tileSize float64 - CharPadding geo.Vec2 - Space, Tab, Line float64 - chars []Character -} - -type jsonChar struct { - Char string - X, Y, Offset float64 -} - -// Creates a new font from texture. Characters must be added afterwards. -// The characters must be placed within a grid, -// the second parameter describes the width and height of one tile in pixel. -func NewFont(tex *dp.Tex, tileSize int) *Font { - font := &Font{} +// Creates a new font for given texture. +// The tile size specifies the size of one character tile on texture. +// Characters must be added afterwards. +func NewFont(tex *Tex, tileSize float64) *Font { + font := Font{} font.Tex = tex - font.tileSize = float64(tileSize) - font.CharPadding = geo.Vec2{0.05, 0.05} + font.tileSize = tileSize + font.CharPadding = Vec2{0.05, 0.05} font.Space = 0.3 font.Tab = 1.2 font.Line = 1 - font.chars = make([]Character, 0) + font.chars = make([]character, 0) - return font + return &font +} + +// Returns a new renderable text object. +func NewText(font *Font, textStr string) *Text { + text := Text{} + text.Actor = NewActor() + text.Pos2D = NewPos2D() + text.TextComponent = &TextComponent{} + text.index = NewVBO(gl.ELEMENT_ARRAY_BUFFER) + text.vertex = NewVBO(gl.ARRAY_BUFFER) + text.texCoord = NewVBO(gl.ARRAY_BUFFER) + text.vao = NewVAO() + text.SetText(font, textStr) + text.Color = Vec4{1, 1, 1, 1} + text.Size = Vec2{1, 1} + text.Scale = Vec2{1, 1} + text.Visible = true + + return &text +} + +// Creates a new text renderer using given shader, camera and font. +// If shader and/or camera are nil, the default one will be used. +func NewTextRenderer(shader *Shader, camera *Camera, font *Font) *TextRenderer { + if shader == nil { + shader = DefaultTextShader + } + + if camera == nil { + camera = DefaultCamera + } + + renderer := &TextRenderer{} + renderer.Shader = shader + renderer.Camera = camera + renderer.Font = font + renderer.texts = make([]Text, 0) + renderer.Size = Vec2{1, 1} + renderer.Scale = Vec2{1, 1} + + return renderer } // Loads characters from JSON file. @@ -124,7 +135,7 @@ func NewFont(tex *dp.Tex, tileSize int) *Font { // Where x and y start in the upper left corner of the texture, both of type int. // Offset is optional and can be used to move a character up or down (relative to others). // If cut is set to true, the characters will be true typed. -func (f *Font) LoadFromJson(path string, cut bool) error { +func (f *Font) FromJson(path string, cut bool) error { // load file content content, err := ioutil.ReadFile(path) @@ -150,21 +161,22 @@ func (f *Font) extractChars(chars []jsonChar, cut bool) { continue } - var min, max, size geo.Vec2 + var min, max, size Vec2 if !cut { - min = geo.Vec2{char.X * f.tileSize, char.Y * f.tileSize} - max = geo.Vec2{min.X + f.tileSize, min.Y + f.tileSize} - size = geo.Vec2{1, 1} + min = Vec2{char.X * f.tileSize, char.Y * f.tileSize} + max = Vec2{min.X + f.tileSize, min.Y + f.tileSize} + size = Vec2{1, 1} } else { min, max, size = f.cutChar(int(char.X), int(char.Y)) } - f.chars = append(f.chars, Character{char.Char[0], min, max, size, char.Offset}) + f.chars = append(f.chars, character{char.Char[0], min, max, size, char.Offset}) } } -func (f *Font) cutChar(x, y int) (geo.Vec2, geo.Vec2, geo.Vec2) { +func (f *Font) cutChar(x, y int) (Vec2, Vec2, Vec2) { + // find min/max corners of character on texture minX := int(f.Tex.GetSize().X) minY := int(f.Tex.GetSize().Y) maxX := 0 @@ -193,22 +205,21 @@ func (f *Font) cutChar(x, y int) (geo.Vec2, geo.Vec2, geo.Vec2) { } } + // add padding minX -= char_padding maxX += char_padding minY -= char_padding maxY += char_padding texSize := f.Tex.GetSize() - min := geo.Vec2{float64(minX) / texSize.X, float64(maxY) / texSize.Y} - max := geo.Vec2{float64(maxX) / texSize.X, float64(minY) / texSize.Y} - - // size - size := geo.Vec2{float64(maxX-minX) / f.tileSize, float64(maxY-minY) / f.tileSize} + min := Vec2{float64(minX) / texSize.X, float64(maxY) / texSize.Y} + max := Vec2{float64(maxX) / texSize.X, float64(minY) / texSize.Y} + size := Vec2{float64(maxX-minX) / f.tileSize, float64(maxY-minY) / f.tileSize} return min, max, size } -func (f *Font) getChar(char byte) *Character { +func (f *Font) getChar(char byte) *character { for _, character := range f.chars { if character.char == char { return &character @@ -218,20 +229,8 @@ func (f *Font) getChar(char byte) *Character { return nil } -type Text struct { - *Actor - *Pos2D - - id int - text string - bounds geo.Vec2 - - index, vertex, texCoord *dp.VBO - vao *dp.VAO -} - -// Deletes GL buffers bound to this text. -func (t *Text) Drop() { +// Deletes GL buffers bound to this text component. +func (t *TextComponent) Drop() { t.index.Drop() t.vertex.Drop() t.texCoord.Drop() @@ -241,7 +240,6 @@ func (t *Text) Drop() { // Sets the given string as text and (re)creates buffers. func (t *Text) SetText(font *Font, text string) { t.text = text - indices := make([]uint32, len(text)*6) vertices := make([]float32, len(text)*8) texCoords := make([]float32, len(text)*8) @@ -263,7 +261,7 @@ func (t *Text) SetText(font *Font, text string) { // create vertices/texCoords index = 0 - offset := geo.Vec2{} + offset := Vec2{} var width, height float64 for i := 0; i < len(text)*8 && int(index) < len(text); i += 8 { @@ -327,18 +325,14 @@ func (t *Text) SetText(font *Font, text string) { } } - t.bounds = geo.Vec2{width, height} + t.bounds = Vec2{width, height} // fill GL buffer t.index.Fill(gl.Ptr(indices[:chars*6]), 4, chars*6, gl.STATIC_DRAW) t.vertex.Fill(gl.Ptr(vertices[:chars*8]), 4, chars*8, gl.STATIC_DRAW) t.texCoord.Fill(gl.Ptr(texCoords[:chars*8]), 4, chars*8, gl.STATIC_DRAW) - util.CheckGLError() -} - -func (t *Text) GetId() int { - return t.id + CheckGLError() } // Returns the text as string. @@ -347,87 +341,82 @@ func (t *Text) GetText() string { } // Returns bounds of text, which is the size of characters. -func (t *Text) GetBounds() geo.Vec2 { - return geo.Vec2{t.bounds.X * t.Size.X * t.Scale.X, t.bounds.Y * t.Size.Y * t.Scale.Y} +func (t *Text) GetBounds() Vec2 { + return Vec2{t.bounds.X * t.Size.X * t.Scale.X, t.bounds.Y * t.Size.Y * t.Scale.Y} } -type TextRenderer struct { - Pos2D - - Shader *dp.Shader - Camera *Camera - Font *Font - Color geo.Vec4 - texts []*Text -} - -// Creates a new text renderer using given shader, camera and font. -// If shader and/or camera are nil, the default one will be used. -func NewTextRenderer(shader *dp.Shader, camera *Camera, font *Font) *TextRenderer { - renderer := &TextRenderer{} - renderer.Shader = shader - renderer.Camera = camera - renderer.Font = font - renderer.Color = geo.Vec4{1, 1, 1, 1} - renderer.texts = make([]*Text, 0) - renderer.Size = geo.Vec2{1, 1} - renderer.Scale = geo.Vec2{1, 1} - - return renderer -} - -// Prepares a text for rendering. +// Prepares given text for rendering. func (r *TextRenderer) Prepare(text *Text) { - text.vao = dp.NewVAO() + text.vao = NewVAO() text.vao.Bind() r.Shader.EnableVertexAttribArrays() text.index.Bind() text.vertex.Bind() - text.vertex.AttribPointer(r.Shader.GetAttribLocation(TEXTRENDERER_VERTEX_ATTRIB), 2, gl.FLOAT, false, 0) + text.vertex.AttribPointer(r.Shader.GetAttribLocation(Default_shader_text_vertex_attrib), 2, gl.FLOAT, false, 0) text.texCoord.Bind() - text.texCoord.AttribPointer(r.Shader.GetAttribLocation(TEXTRENDERER_TEXCOORD_ATTRIB), 2, gl.FLOAT, false, 0) + text.texCoord.AttribPointer(r.Shader.GetAttribLocation(Default_shader_text_texcoord_attrib), 2, gl.FLOAT, false, 0) text.vao.Unbind() } -// Adds text to the renderer. -func (r *TextRenderer) Add(text *Text) { - r.texts = append(r.texts, text) +// Frees recources created by text component. +// This is called automatically when system gets removed. +func (r *TextRenderer) Cleanup() { + for _, text := range r.texts { + text.Drop() + } } -// Returns text by ID. -func (r *TextRenderer) Get(id int) *Text { +// Adds text to the renderer. +func (r *TextRenderer) Add(actor *Actor, pos *Pos2D, text *TextComponent) bool { + id := actor.GetId() + for _, text := range r.texts { - if text.GetId() == id { - return text + if id == text.Actor.GetId() { + return false } } - return nil + r.texts = append(r.texts, Text{actor, pos, text}) + + return true +} + +// Removes text from renderer. +func (r *TextRenderer) Remove(actor *Actor) bool { + return r.RemoveById(actor.GetId()) } // Removes text from renderer by ID. -func (r *TextRenderer) Remove(id int) *Text { +func (r *TextRenderer) RemoveById(id ActorId) bool { for i, text := range r.texts { - if text.GetId() == id { + if text.Actor.GetId() == id { r.texts = append(r.texts[:i], r.texts[i+1:]...) - return text + return true } } - return nil + return false } -// Removes all sprites. -func (r *TextRenderer) Clear() { - r.texts = make([]*Text, 0) +// Removes all texts. +func (r *TextRenderer) RemoveAll() { + r.texts = make([]Text, 0) } -// Renders sprites. -func (r *TextRenderer) Render() { +// Returns number of texts. +func (r *TextRenderer) Len() int { + return len(r.texts) +} + +func (r *TextRenderer) GetName() string { + return text_renderer_name +} + +// Renders texts. +func (r *TextRenderer) Update(delta float64) { r.Shader.Bind() - r.Shader.SendMat3(TEXTRENDERER_ORTHO, *geo.MultMat3(r.Camera.CalcOrtho(), r.CalcModel())) - r.Shader.SendUniform1i(TEXTRENDERER_TEX, 0) - r.Shader.SendUniform4f(TEXTRENDERER_COLOR, float32(r.Color.X), float32(r.Color.Y), float32(r.Color.Z), float32(r.Color.W)) + r.Shader.SendMat3(Default_shader_text_ortho, *MultMat3(r.Camera.CalcOrtho(), r.CalcModel())) + r.Shader.SendUniform1i(Default_shader_text_tex, 0) r.Font.Tex.Bind() for i := range r.texts { @@ -436,9 +425,9 @@ func (r *TextRenderer) Render() { } r.texts[i].vao.Bind() - r.Shader.SendMat3(TEXTRENDERER_MODEL, *r.texts[i].CalcModel()) + r.Shader.SendUniform4f(Default_shader_text_color, float32(r.texts[i].Color.X), float32(r.texts[i].Color.Y), float32(r.texts[i].Color.Z), float32(r.texts[i].Color.W)) + r.Shader.SendMat3(Default_shader_text_model, *r.texts[i].CalcModel()) gl.DrawElements(gl.TRIANGLES, r.texts[i].index.Size(), gl.UNSIGNED_INT, nil) } } -*/