1 Commits

Author SHA1 Message Date
Marvin Blum
6e368f066f Started texture mapped fonts demo. 2016-05-21 13:53:27 +02:00
33 changed files with 381 additions and 759 deletions

View File

@@ -1,10 +0,0 @@
# Changelog
## 0.2_beta
* code restructuring
* added system access functions (like GetSpriteRenderer())
## 0.1_beta
* beta release

View File

@@ -1,14 +1,15 @@
# go-game (package "goga") # go-game (package "goga")
Game engine written in Go using OpenGL and GLFW. Mostly for 2D rendering, but also capable of rendering 3D, providing everything to get you started. Game engine written in Go using OpenGL and GLFW. Mostly for 2D rendering, but also capable of rendering 3D, providing everything to get you started.
**Under heavy development, do not use yet!**
## Install ## Install
go-game requires OpenGL and GLFW. The following three steps install everything you need: go-game requires OpenGL and GLFW. The following three steps install everything you need:
``` ```
go get github.com/go-gl/gl/v3.2-core/gl go get github.com/go-gl/gl/v4.5-core/gl
go get github.com/go-gl/glfw/v3.2/glfw go get github.com/go-gl/glfw/v3.1/glfw
go get github.com/DeKugelschieber/go-game go get github.com/DeKugelschieber/go-game
``` ```
@@ -16,16 +17,18 @@ You also need a cgo compiler (typically gcc) and GL/GLFW development libraries a
## Usage ## Usage
*WIP*
Examples can be found within the demo folder. For full reference visit: https://godoc.org/github.com/DeKugelschieber/go-game Examples can be found within the demo folder. For full reference visit: https://godoc.org/github.com/DeKugelschieber/go-game
## Dependencies ## Dependencies
* https://github.com/go-gl/gl * https://github.com/go-gl/gl
- 3.1-core - 4.5-core
* https://github.com/go-gl/glfw * https://github.com/go-gl/glfw
- 3.1 - 3.1
To use a different GL version, you need to replace the GL imports in package goga. To use an older GL version, you need to replace the GL imports in package goga. It should mostly be compatible down to 3.x.
## Contribute ## Contribute

View File

@@ -2,9 +2,6 @@
* ~~cleanup resources~~ * ~~cleanup resources~~
* more logging * more logging
* ~~limit FPS~~ * limit FPS
- does this really work? * fullscreen
* ~~fullscreen~~ * simple access to default resources like GetTex()
* ~~simple access to default resources like GetTex()~~
* ~~text rendering + font loading~~
* more options for GLFW

View File

@@ -1,8 +1,6 @@
package goga package goga
var ( import ()
actorIdGen = ActorId(0)
)
// An actor ID is a unique integer, // An actor ID is a unique integer,
// which can be used to reference an actor. // which can be used to reference an actor.
@@ -14,6 +12,10 @@ type Actor struct {
id ActorId id ActorId
} }
var (
actorIdGen = ActorId(0)
)
// Creates a new basic actor with unique ID. // Creates a new basic actor with unique ID.
func NewActor() *Actor { func NewActor() *Actor {
actorIdGen++ actorIdGen++

View File

@@ -80,14 +80,14 @@ func (c *Culling2D) GetName() string {
// Updates visibility of all contained sprites. // Updates visibility of all contained sprites.
func (c *Culling2D) Update(delta float64) { func (c *Culling2D) Update(delta float64) {
for _, cullable := range c.cullables { for i := range c.cullables {
if cullable.Pos.X > c.viewport.Z || if c.cullables[i].Pos.X > c.viewport.Z ||
cullable.Pos.X+cullable.Size.X < c.viewport.X || c.cullables[i].Pos.X+c.cullables[i].Size.X < c.viewport.X ||
cullable.Pos.Y > c.viewport.W || c.cullables[i].Pos.Y > c.viewport.W ||
cullable.Pos.Y+cullable.Size.Y < c.viewport.Y { c.cullables[i].Pos.Y+c.cullables[i].Size.Y < c.viewport.Y {
cullable.Visible = false c.cullables[i].Visible = false
} else { } else {
cullable.Visible = true c.cullables[i].Visible = true
} }
} }
} }

View File

@@ -1,83 +0,0 @@
package goga
const (
// constants for default 2D shader
Default_shader_2D_vertex_attrib = "vertex"
Default_shader_2D_texcoord_attrib = "texCoord"
Default_shader_2D_ortho = "o"
Default_shader_2D_model = "m"
Default_shader_2D_tex = "tex"
// source for 2D shader
default_shader_2d_vertex_src = `#version 130
uniform mat3 o, m;
in vec2 vertex;
in vec2 texCoord;
out vec2 tc;
void main(){
tc = texCoord;
gl_Position = vec4(o*m*vec3(vertex, 1.0), 1.0);
}`
default_shader_2d_fragment_src = `#version 130
precision highp float;
uniform sampler2D tex;
in vec2 tc;
out vec4 color;
void main(){
color = texture(tex, tc);
}`
// constants for default 3D shader
Default_shader_3D_vertex_attrib = "vertex"
Default_shader_3D_texcoord_attrib = "texCoord"
Default_shader_3D_pv = "pv"
Default_shader_3D_model = "m"
Default_shader_3D_tex = "tex"
// source for 3D shader
default_shader_3d_vertex_src = `#version 130
uniform mat4 pv, m;
in vec3 vertex;
in vec2 texCoord;
out vec2 tc;
void main(){
tc = texCoord;
gl_Position = pv*m*vec4(vertex, 1.0);
}`
default_shader_3d_fragment_src = `#version 130
precision highp float;
uniform sampler2D tex;
in vec2 tc;
out vec4 color;
void main(){
color = texture(tex, tc);
}`
// constants for default text shader
Default_shader_text_vertex_attrib = "vertex"
Default_shader_text_texcoord_attrib = "texCoord"
Default_shader_text_ortho = "o"
Default_shader_text_model = "m"
Default_shader_text_tex = "tex"
Default_shader_text_color = "color"
// source for text shader
default_shader_text_vertex_src = `#version 130
uniform mat3 o, m;
in vec2 vertex;
in vec2 texCoord;
out vec2 tc;
void main(){
tc = texCoord;
gl_Position = vec4(o*m*vec3(vertex, 1.0), 1.0);
}`
default_shader_text_fragment_src = `#version 130
precision highp float;
uniform sampler2D tex;
uniform vec4 color;
in vec2 tc;
out vec4 c;
void main(){
c = texture(tex, tc)*color;
}`
)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

View File

@@ -1,77 +0,0 @@
package main
import (
"github.com/DeKugelschieber/go-game"
"github.com/go-gl/glfw/v3.2/glfw"
)
const (
gopher_path = "src/github.com/DeKugelschieber/go-game/demo/input/assets/gopher.png"
)
type Game struct {
mouseX, mouseY float64
sprite *goga.Sprite
}
func (g *Game) Setup() {
// load texture
_, err := goga.LoadRes(gopher_path)
if err != nil {
panic(err)
}
// create sprite
tex, err := goga.GetTex("gopher.png")
if err != nil {
panic(err)
}
sprite := goga.NewSprite(tex)
sprite.Size.X = sprite.Size.X / 4
sprite.Size.Y = sprite.Size.Y / 4
g.sprite = sprite
renderer := goga.GetSpriteRenderer()
renderer.Add(sprite.Actor, sprite.Pos2D, sprite.Tex)
culling := goga.GetCulling2DSystem()
culling.Add(sprite.Actor, sprite.Pos2D)
// register input listeners
goga.AddKeyboardListener(g)
goga.AddMouseListener(g)
}
func (g *Game) Update(delta float64) {}
func (g *Game) OnKeyEvent(key glfw.Key, code int, action glfw.Action, mod glfw.ModifierKey) {
// ESC
if key == 256 {
goga.Stop()
}
}
func (g *Game) OnMouseButton(button glfw.MouseButton, action glfw.Action, mod glfw.ModifierKey) {
if button == 0 {
g.sprite.Pos.X = g.mouseX
g.sprite.Pos.Y = g.mouseY
}
}
func (g *Game) OnMouseMove(x float64, y float64) {
g.mouseX = x
g.mouseY = y
}
func (g *Game) OnMouseScroll(x float64, y float64) {}
func main() {
game := Game{}
options := goga.RunOptions{ClearColor: goga.Vec4{1, 1, 1, 0},
Resizable: true,
SetViewportOnResize: true,
ExitOnClose: true}
goga.Run(&game, &options)
}

View File

@@ -39,7 +39,12 @@ func (g *Game) Setup() {
sprite.KeyframeAnimation = goga.NewKeyframeAnimation(0, 7, true, 20) sprite.KeyframeAnimation = goga.NewKeyframeAnimation(0, 7, true, 20)
// add to renderer // add to renderer
renderer := goga.GetKeyframeRenderer() renderer, ok := goga.GetSystemByName("keyframeRenderer").(*goga.KeyframeRenderer)
if !ok {
panic("Could not find renderer")
}
renderer.Add(sprite.Actor, sprite.Pos2D, sprite.Tex, sprite.KeyframeSet, sprite.KeyframeAnimation) renderer.Add(sprite.Actor, sprite.Pos2D, sprite.Tex, sprite.KeyframeSet, sprite.KeyframeAnimation)
} }
@@ -50,7 +55,6 @@ func main() {
options := goga.RunOptions{ClearColor: goga.Vec4{0, 0, 0, 0}, options := goga.RunOptions{ClearColor: goga.Vec4{0, 0, 0, 0},
Resizable: true, Resizable: true,
SetViewportOnResize: true, SetViewportOnResize: true,
ExitOnClose: true, ExitOnClose: true}
Fullscreen: true}
goga.Run(&game, &options) goga.Run(&game, &options)
} }

View File

@@ -36,7 +36,12 @@ func (g *Game) Setup() {
mesh := goga.NewMesh(ply.IndexBuffer, ply.VertexBuffer, ply.TexCoordBuffer) mesh := goga.NewMesh(ply.IndexBuffer, ply.VertexBuffer, ply.TexCoordBuffer)
model := goga.NewModel(mesh, tex) model := goga.NewModel(mesh, tex)
renderer := goga.GetModelRenderer() renderer, ok := goga.GetSystemByName("modelRenderer").(*goga.ModelRenderer)
if !ok {
panic("Could not find renderer")
}
renderer.Prepare(model) renderer.Prepare(model)
renderer.Add(model.Actor, model.Pos3D, model.Tex, model.Mesh) renderer.Add(model.Actor, model.Pos3D, model.Tex, model.Mesh)
g.model = model g.model = model

View File

@@ -26,10 +26,20 @@ func (g *Game) Setup() {
} }
sprite := goga.NewSprite(tex) sprite := goga.NewSprite(tex)
renderer := goga.GetSpriteRenderer() renderer, ok := goga.GetSystemByName("spriteRenderer").(*goga.SpriteRenderer)
if !ok {
panic("Could not find renderer")
}
renderer.Add(sprite.Actor, sprite.Pos2D, sprite.Tex) renderer.Add(sprite.Actor, sprite.Pos2D, sprite.Tex)
culling := goga.GetCulling2DSystem() culling, ok := goga.GetSystemByName("culling2d").(*goga.Culling2D)
if !ok {
panic("Could not find culling")
}
culling.Add(sprite.Actor, sprite.Pos2D) culling.Add(sprite.Actor, sprite.Pos2D)
} }

View File

@@ -0,0 +1,28 @@
[
{
"char": "A",
"x": 0,
"y": 0
},
{
"char": "b",
"x": 1,
"y": 0
},
{
"char": "e",
"x": 2,
"y": 0
},
{
"char": "r",
"x": 0,
"y": 1
},
{
"char": "'",
"x": 3,
"y": 0,
"offset": 0.5
}
]

BIN
demo/text/assets/dejavu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -1,113 +0,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":"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}
]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -2,65 +2,52 @@ package main
import ( import (
"github.com/DeKugelschieber/go-game" "github.com/DeKugelschieber/go-game"
"github.com/go-gl/gl/v3.2-core/gl"
) )
const ( const (
font_path = "src/github.com/DeKugelschieber/go-game/demo/text/assets/victor.png" gopher_path = "src/github.com/DeKugelschieber/go-game/demo/sprite/assets/gopher.png"
font_json = "src/github.com/DeKugelschieber/go-game/demo/text/assets/victor.json"
) )
type Game struct{} type Game struct{}
func (g *Game) Setup() { func (g *Game) Setup() {
// load texture // load texture
pngLoader, ok := goga.GetLoaderByExt("png").(*goga.PngLoader) _, err := goga.LoadRes(gopher_path)
if err != nil {
panic(err)
}
// create sprite
tex, err := goga.GetTex("gopher.png")
if err != nil {
panic(err)
}
sprite := goga.NewSprite(tex)
renderer, ok := goga.GetSystemByName("spriteRenderer").(*goga.SpriteRenderer)
if !ok { if !ok {
panic("Could not get PNG loader") panic("Could not find renderer")
} }
pngLoader.KeepData = true renderer.Add(sprite.Actor, sprite.Pos2D, sprite.Tex)
pngLoader.Filter = gl.NEAREST
_, err := goga.LoadRes(font_path)
if err != nil { culling, ok := goga.GetSystemByName("culling2d").(*goga.Culling2D)
panic(err)
if !ok {
panic("Could not find culling")
} }
pngLoader.KeepData = false culling.Add(sprite.Actor, sprite.Pos2D)
pngLoader.Filter = gl.LINEAR
// create font
tex, err := goga.GetTex("victor.png")
if err != nil {
panic(err)
}
font := goga.NewFont(tex, 16)
if err := font.FromJson(font_json, true); err != nil {
panic(err)
}
// setup renderer
renderer := goga.GetTextRenderer()
renderer.Font = font
// 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) {} func (g *Game) Update(delta float64) {}
func main() { func main() {
game := Game{} game := Game{}
options := goga.RunOptions{ClearColor: goga.Vec4{0, 0, 0, 0}, options := goga.RunOptions{ClearColor: goga.Vec4{1, 1, 1, 0},
Resizable: true, Resizable: true,
SetViewportOnResize: true, SetViewportOnResize: true,
ExitOnClose: true} ExitOnClose: true}

View File

@@ -7,7 +7,7 @@ type Dropable interface {
} }
// Drops given GL objects. // Drops given GL objects.
// Objects must implement the Dropable interface. // Objects must implement the Dropable inteface.
func Drop(objects []Dropable) { func Drop(objects []Dropable) {
for _, obj := range objects { for _, obj := range objects {
obj.Drop() obj.Drop()

2
fbo.go
View File

@@ -1,7 +1,7 @@
package goga package goga
import ( import (
"github.com/go-gl/gl/v3.2-core/gl" "github.com/go-gl/gl/v4.5-core/gl"
) )
// Frame Buffer Object. // Frame Buffer Object.

155
game.go
View File

@@ -1,8 +1,8 @@
package goga package goga
import ( import (
"github.com/go-gl/gl/v3.2-core/gl" "github.com/go-gl/gl/v4.5-core/gl"
"github.com/go-gl/glfw/v3.2/glfw" "github.com/go-gl/glfw/v3.1/glfw"
"log" "log"
"math" "math"
"runtime" "runtime"
@@ -14,20 +14,86 @@ const (
default_height = uint32(400) default_height = uint32(400)
default_title = "Game" default_title = "Game"
default_exit_on_close = true default_exit_on_close = true
)
var ( // constants for default 2D shader
running = true Default_shader_2D_vertex_attrib = "vertex"
clearColor = Vec4{} Default_shader_2D_texcoord_attrib = "texCoord"
clearBuffer []uint32 Default_shader_2D_ortho = "o"
viewportWidth int Default_shader_2D_model = "m"
viewportHeight int Default_shader_2D_tex = "tex"
// Default resources // source for 2D shader
DefaultCamera *Camera default_shader_2d_vertex_src = `#version 130
Default2DShader *Shader uniform mat3 o, m;
Default3DShader *Shader in vec2 vertex;
DefaultTextShader *Shader in vec2 texCoord;
out vec2 tc;
void main(){
tc = texCoord;
gl_Position = vec4(o*m*vec3(vertex, 1.0), 1.0);
}`
default_shader_2d_fragment_src = `#version 130
precision highp float;
uniform sampler2D tex;
in vec2 tc;
out vec4 color;
void main(){
color = texture(tex, tc);
}`
// constants for default 3D shader
Default_shader_3D_vertex_attrib = "vertex"
Default_shader_3D_texcoord_attrib = "texCoord"
Default_shader_3D_pv = "pv"
Default_shader_3D_model = "m"
Default_shader_3D_tex = "tex"
// source for 3D shader
default_shader_3d_vertex_src = `#version 130
uniform mat4 pv, m;
in vec3 vertex;
in vec2 texCoord;
out vec2 tc;
void main(){
tc = texCoord;
gl_Position = pv*m*vec4(vertex, 1.0);
}`
default_shader_3d_fragment_src = `#version 130
precision highp float;
uniform sampler2D tex;
in vec2 tc;
out vec4 color;
void main(){
color = texture(tex, tc);
}`
// constants for default text shader
Default_shader_text_vertex_attrib = "vertex"
Default_shader_text_texcoord_attrib = "texCoord"
Default_shader_text_ortho = "o"
Default_shader_text_model = "m"
Default_shader_text_tex = "tex"
Default_shader_text_color = "color"
// source for text shader
default_shader_text_vertex_src = `#version 130
uniform mat3 o, m;
in vec2 vertex;
in vec2 texCoord;
out vec2 tc;
void main(){
tc = texCoord;
gl_Position = vec4(o*m*vec3(vertex, 1.0), 1.0);
}`
default_shader_text_fragment_src = `#version 130
precision highp float;
uniform sampler2D tex;
uniform vec4 color;
in vec2 tc;
out vec4 c;
void main(){
c = texture(tex, tc)*color;
}`
) )
// If set in RunOptions, the function will be called on window resize. // If set in RunOptions, the function will be called on window resize.
@@ -43,9 +109,6 @@ type RunOptions struct {
SetViewportOnResize bool SetViewportOnResize bool
ResizeCallbackFunc ResizeCallback ResizeCallbackFunc ResizeCallback
ExitOnClose bool ExitOnClose bool
RefreshRate int
Fullscreen bool
MonitorId uint // index
} }
// Main game object. // Main game object.
@@ -57,9 +120,22 @@ type Game interface {
Update(float64) Update(float64)
} }
var (
running = true
clearColor = Vec4{}
clearBuffer []uint32
viewportWidth int
viewportHeight int
// Default resources
DefaultCamera *Camera
Default2DShader *Shader
Default3DShader *Shader
DefaultTextShader *Shader
)
func init() { func init() {
// GL functions must be called from main thread, // GL functions must be called from main thread.
// so we disable multithreading by the runtime here.
log.Print("Locking OS thread") log.Print("Locking OS thread")
runtime.LockOSThread() runtime.LockOSThread()
} }
@@ -92,52 +168,32 @@ func Run(game Game, options *RunOptions) {
title := default_title title := default_title
exitOnClose := default_exit_on_close exitOnClose := default_exit_on_close
if options != nil { if options != nil && options.Width > 0 {
if options.Width > 0 {
width = options.Width width = options.Width
} }
if options.Height > 0 { if options != nil && options.Height > 0 {
height = options.Height height = options.Height
} }
if len(options.Title) > 0 { if options != nil && len(options.Title) > 0 {
title = options.Title title = options.Title
} }
if options != nil {
exitOnClose = options.ExitOnClose exitOnClose = options.ExitOnClose
}
if !options.Resizable { if options != nil && !options.Resizable {
glfw.WindowHint(glfw.Resizable, glfw.False) glfw.WindowHint(glfw.Resizable, glfw.False)
} }
}
var monitor *glfw.Monitor wnd, err := glfw.CreateWindow(int(width), int(height), title, nil, nil)
if options != nil && options.Fullscreen {
monitors := glfw.GetMonitors()
if int(options.MonitorId) < len(monitors) {
monitor = monitors[options.MonitorId]
}
}
if monitor != nil {
videoMode := monitor.GetVideoMode()
width = uint32(videoMode.Width)
height = uint32(videoMode.Height)
}
wnd, err := glfw.CreateWindow(int(width), int(height), title, monitor, nil)
if err != nil { if err != nil {
panic("Error creating GLFW window: " + err.Error()) panic("Error creating GLFW window: " + err.Error())
} }
if options != nil && options.RefreshRate != 0 {
glfw.WindowHint(glfw.RefreshRate, options.RefreshRate)
}
// window event handlers // window event handlers
wnd.SetSizeCallback(func(w *glfw.Window, width, height int) { wnd.SetSizeCallback(func(w *glfw.Window, width, height int) {
if options == nil { if options == nil {
@@ -155,8 +211,6 @@ func Run(game Game, options *RunOptions) {
} }
}) })
initInput(wnd)
// make GL context current // make GL context current
wnd.MakeContextCurrent() wnd.MakeContextCurrent()
@@ -181,8 +235,8 @@ func Run(game Game, options *RunOptions) {
delta := time.Duration(0) delta := time.Duration(0)
var deltaSec float64 var deltaSec float64
for { for running {
if !running || exitOnClose && wnd.ShouldClose() { if exitOnClose && wnd.ShouldClose() {
cleanup() cleanup()
return return
} }
@@ -256,7 +310,6 @@ func initGoga(width, height int) {
AddSystem(NewModelRenderer(nil, nil, false)) AddSystem(NewModelRenderer(nil, nil, false))
AddSystem(NewCulling2D(0, 0, width, height)) AddSystem(NewCulling2D(0, 0, width, height))
AddSystem(NewKeyframeRenderer(nil, nil)) AddSystem(NewKeyframeRenderer(nil, nil))
AddSystem(NewTextRenderer(nil, nil, nil)) // font must be set outside!
} }
func cleanup() { func cleanup() {

View File

@@ -1,7 +1,7 @@
package goga package goga
import ( import (
"github.com/go-gl/gl/v3.2-core/gl" "github.com/go-gl/gl/v4.5-core/gl"
"log" "log"
) )

View File

@@ -1,98 +0,0 @@
package goga
import (
"github.com/go-gl/glfw/v3.2/glfw"
)
var (
keyboardListener []KeyboardListener
mouseListener []MouseListener
)
// Interface for keyboard input events.
// Implement and register to receive keyboard input.
type KeyboardListener interface {
OnKeyEvent(glfw.Key, int, glfw.Action, glfw.ModifierKey)
}
// Interface for mouse input events.
// Implement and register to receive mouse input.
type MouseListener interface {
OnMouseButton(glfw.MouseButton, glfw.Action, glfw.ModifierKey)
OnMouseMove(float64, float64)
OnMouseScroll(float64, float64)
}
func initInput(wnd *glfw.Window) {
wnd.SetKeyCallback(keyboardCallback)
wnd.SetMouseButtonCallback(mouseButtonCallback)
wnd.SetCursorPosCallback(mouseMoveCallback)
wnd.SetScrollCallback(mouseScrollCallback)
keyboardListener = make([]KeyboardListener, 0)
mouseListener = make([]MouseListener, 0)
}
func keyboardCallback(wnd *glfw.Window, key glfw.Key, code int, action glfw.Action, mod glfw.ModifierKey) {
for _, listener := range keyboardListener {
listener.OnKeyEvent(key, code, action, mod)
}
}
func mouseButtonCallback(wnd *glfw.Window, button glfw.MouseButton, action glfw.Action, mod glfw.ModifierKey) {
for _, listener := range mouseListener {
listener.OnMouseButton(button, action, mod)
}
}
func mouseMoveCallback(wnd *glfw.Window, x float64, y float64) {
for _, listener := range mouseListener {
listener.OnMouseMove(x, float64(viewportHeight)-y)
}
}
func mouseScrollCallback(wnd *glfw.Window, x float64, y float64) {
for _, listener := range mouseListener {
listener.OnMouseScroll(x, y)
}
}
// Adds a new keyboard listener.
func AddKeyboardListener(listener KeyboardListener) {
keyboardListener = append(keyboardListener, listener)
}
// Removes given keyboard listener if found.
func RemoveKeyboardListener(listener KeyboardListener) {
for i, l := range keyboardListener {
if l == listener {
keyboardListener = append(keyboardListener[:i], keyboardListener[i+1:]...)
return
}
}
}
// Removes all registered keyboard listeners.
func RemoveAllKeyboardListener() {
keyboardListener = make([]KeyboardListener, 0)
}
// Adds a new mouse listener.
func AddMouseListener(listener MouseListener) {
mouseListener = append(mouseListener, listener)
}
// Removes given mouse listener if found.
func RemoveMouseListener(listener MouseListener) {
for i, l := range mouseListener {
if l == listener {
mouseListener = append(mouseListener[:i], mouseListener[i+1:]...)
return
}
}
}
// Removes all registered mouse listeners.
func RemoveAllMouseListener() {
mouseListener = make([]MouseListener, 0)
}

View File

@@ -1,7 +1,7 @@
package goga package goga
import ( import (
"github.com/go-gl/gl/v3.2-core/gl" "github.com/go-gl/gl/v4.5-core/gl"
) )
const ( const (
@@ -206,22 +206,22 @@ func (s *KeyframeRenderer) GetName() string {
// Updates animation state and renders sprites. // Updates animation state and renders sprites.
func (s *KeyframeRenderer) Update(delta float64) { func (s *KeyframeRenderer) Update(delta float64) {
// update animation state // update animation state
for _, sprite := range s.sprites { for i := range s.sprites {
if sprite.KeyframeAnimation == nil { if s.sprites[i].KeyframeAnimation == nil {
continue continue
} }
sprite.Interpolation += delta * sprite.KeyframeAnimation.Speed s.sprites[i].Interpolation += delta * s.sprites[i].KeyframeAnimation.Speed
if sprite.Interpolation > 1 { if s.sprites[i].Interpolation > 1 {
sprite.Interpolation = 0 s.sprites[i].Interpolation = 0
sprite.Current++ s.sprites[i].Current++
if sprite.Current > sprite.KeyframeAnimation.End { if s.sprites[i].Current > s.sprites[i].KeyframeAnimation.End {
if sprite.KeyframeAnimation.Loop { if s.sprites[i].KeyframeAnimation.Loop {
sprite.Current = sprite.KeyframeAnimation.Start s.sprites[i].Current = s.sprites[i].KeyframeAnimation.Start
} else { } else {
sprite.Current = sprite.KeyframeAnimation.End s.sprites[i].Current = s.sprites[i].KeyframeAnimation.End
} }
} }
} }
@@ -233,17 +233,17 @@ func (s *KeyframeRenderer) Update(delta float64) {
s.Shader.SendUniform1i(Default_shader_2D_tex, 0) s.Shader.SendUniform1i(Default_shader_2D_tex, 0)
s.vao.Bind() s.vao.Bind()
for _, sprite := range s.sprites { for i := range s.sprites {
if !sprite.Visible { if !s.sprites[i].Visible {
continue continue
} }
texCoord := sprite.KeyframeSet.Keyframes[sprite.Current].texCoord texCoord := s.sprites[i].KeyframeSet.Keyframes[s.sprites[i].Current].texCoord
texCoord.Bind() texCoord.Bind()
texCoord.AttribPointer(s.Shader.GetAttribLocation(Default_shader_2D_texcoord_attrib), 2, gl.FLOAT, false, 0) texCoord.AttribPointer(s.Shader.GetAttribLocation(Default_shader_2D_texcoord_attrib), 2, gl.FLOAT, false, 0)
s.Shader.SendMat3(Default_shader_2D_model, *sprite.CalcModel()) s.Shader.SendMat3(Default_shader_2D_model, *s.sprites[i].CalcModel())
sprite.Tex.Bind() s.sprites[i].Tex.Bind()
gl.DrawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, nil) gl.DrawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, nil)
} }

View File

@@ -3,7 +3,7 @@ package goga
import ( import (
"bufio" "bufio"
"errors" "errors"
"github.com/go-gl/gl/v3.2-core/gl" "github.com/go-gl/gl/v4.5-core/gl"
"image" "image"
"image/draw" "image/draw"
"image/png" "image/png"
@@ -75,13 +75,6 @@ type Ply struct {
IndexBuffer, VertexBuffer, TexCoordBuffer, NormalBuffer *VBO IndexBuffer, VertexBuffer, TexCoordBuffer, NormalBuffer *VBO
} }
// Loads ply files and creates VBOs within the Ply resource.
// The indices must be present as triangles.
// Expected type is float32. If it fails to parse, it will panic.
type PlyLoader struct {
VboUsage uint32
}
// Drops contained GL buffers. // Drops contained GL buffers.
func (p *Ply) Drop() { func (p *Ply) Drop() {
if p.IndexBuffer != nil { if p.IndexBuffer != nil {
@@ -131,6 +124,13 @@ func (p *Ply) SetExt(ext string) {
p.ext = ext p.ext = ext
} }
// Loads ply files and creates VBOs within the Ply resource.
// The indices must be present as triangles.
// Expected type is float32. If it fails to parse, it will panic.
type PlyLoader struct {
VboUsage uint32
}
func (p *PlyLoader) Load(file string) (Res, error) { func (p *PlyLoader) Load(file string) (Res, error) {
handle, err := os.Open(file) handle, err := os.Open(file)
defer handle.Close() defer handle.Close()

View File

@@ -1,7 +1,7 @@
package goga package goga
import ( import (
"github.com/go-gl/gl/v3.2-core/gl" "github.com/go-gl/gl/v4.5-core/gl"
) )
const ( const (
@@ -174,20 +174,20 @@ func (s *ModelRenderer) Update(delta float64) {
var tid uint32 var tid uint32
for _, model := range s.models { for i := range s.models {
if !model.Visible { if !s.models[i].Visible {
continue continue
} }
s.Shader.SendMat4(Default_shader_3D_model, *model.CalcModel()) s.Shader.SendMat4(Default_shader_3D_model, *s.models[i].CalcModel())
model.Vao.Bind() s.models[i].Vao.Bind()
// prevent texture switching when not neccessary // prevent texture switching when not neccessary
if tid != model.Tex.GetId() { if tid != s.models[i].Tex.GetId() {
tid = model.Tex.GetId() tid = s.models[i].Tex.GetId()
model.Tex.Bind() s.models[i].Tex.Bind()
} }
gl.DrawElements(gl.TRIANGLES, model.Index.Size(), gl.UNSIGNED_INT, nil) gl.DrawElements(gl.TRIANGLES, s.models[i].Index.Size(), gl.UNSIGNED_INT, nil)
} }
} }

View File

@@ -4,11 +4,6 @@ import (
"log" "log"
) )
var (
scenes []Scene
activeScene Scene
)
// A scene used to switch between game states. // A scene used to switch between game states.
// The Cleanup() method is called when a scene is removed // The Cleanup() method is called when a scene is removed
// or the program is stopped. It can be used to cleanup open resources // or the program is stopped. It can be used to cleanup open resources
@@ -24,6 +19,11 @@ type Scene interface {
GetName() string GetName() string
} }
var (
scenes []Scene
activeScene Scene
)
// Adds a scene to game. // Adds a scene to game.
// Returns false if the scene exists already. // Returns false if the scene exists already.
// The first scene added will be set active. // The first scene added will be set active.

View File

@@ -2,7 +2,7 @@ package goga
import ( import (
"errors" "errors"
"github.com/go-gl/gl/v3.2-core/gl" "github.com/go-gl/gl/v4.5-core/gl"
"log" "log"
"strings" "strings"
) )

View File

@@ -1,7 +1,7 @@
package goga package goga
import ( import (
"github.com/go-gl/gl/v3.2-core/gl" "github.com/go-gl/gl/v4.5-core/gl"
) )
const ( const (
@@ -140,17 +140,17 @@ func (s *SpriteRenderer) Update(delta float64) {
s.vao.Bind() s.vao.Bind()
var tid uint32 var tid uint32
for _, sprite := range s.sprites { for i := range s.sprites {
if !sprite.Visible { if !s.sprites[i].Visible {
continue continue
} }
s.Shader.SendMat3(Default_shader_2D_model, *sprite.CalcModel()) s.Shader.SendMat3(Default_shader_2D_model, *s.sprites[i].CalcModel())
// prevent texture switching when not neccessary // prevent texture switching when not neccessary
if tid != sprite.Tex.GetId() { if tid != s.sprites[i].Tex.GetId() {
tid = sprite.Tex.GetId() tid = s.sprites[i].Tex.GetId()
sprite.Tex.Bind() s.sprites[i].Tex.Bind()
} }
gl.DrawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, nil) gl.DrawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, nil)

View File

@@ -2,10 +2,6 @@ package goga
import () import ()
var (
systems []System
)
// A system provides logic for actors satisfying required components. // A system provides logic for actors satisfying required components.
// They are automatically updated on each frame. // They are automatically updated on each frame.
// When a system is removed from systems, the Cleanup() method will be called. // When a system is removed from systems, the Cleanup() method will be called.
@@ -21,6 +17,10 @@ type System interface {
GetName() string GetName() string
} }
var (
systems []System
)
// Adds a system to the game. // Adds a system to the game.
// Returns false if the system exists already. // Returns false if the system exists already.
func AddSystem(system System) bool { func AddSystem(system System) bool {

View File

@@ -1,51 +0,0 @@
package goga
func GetSpriteRenderer() *SpriteRenderer {
renderer, ok := GetSystemByName(sprite_renderer_name).(*SpriteRenderer)
if !ok {
panic("Could not obtain sprite renderer")
}
return renderer
}
func GetModelRenderer() *ModelRenderer {
renderer, ok := GetSystemByName(model_renderer_name).(*ModelRenderer)
if !ok {
panic("Could not obtain model renderer")
}
return renderer
}
func GetCulling2DSystem() *Culling2D {
system, ok := GetSystemByName(culling_2d_name).(*Culling2D)
if !ok {
panic("Could not obtain culling system")
}
return system
}
func GetKeyframeRenderer() *KeyframeRenderer {
renderer, ok := GetSystemByName(keyframe_sprite_renderer_name).(*KeyframeRenderer)
if !ok {
panic("Could not obtain keyframe renderer")
}
return renderer
}
func GetTextRenderer() *TextRenderer {
renderer, ok := GetSystemByName(text_renderer_name).(*TextRenderer)
if !ok {
panic("Could not obtain text renderer")
}
return renderer
}

2
tex.go
View File

@@ -1,7 +1,7 @@
package goga package goga
import ( import (
"github.com/go-gl/gl/v3.2-core/gl" "github.com/go-gl/gl/v4.5-core/gl"
"image" "image"
) )

265
text.go
View File

@@ -1,51 +1,69 @@
package goga package goga
/*
import ( import (
"core"
"dp"
"encoding/json" "encoding/json"
"github.com/go-gl/gl/v3.2-core/gl" "geo"
"github.com/go-gl/gl/v4.5-core/gl"
"io/ioutil" "io/ioutil"
"util"
) )
const ( const (
char_padding = 2 char_padding = 2
text_renderer_name = "textRenderer"
) )
type character struct { // 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 char byte
min, max, size Vec2 min, max, size geo.Vec2
offset float64 offset float64
} }
type Font struct {
Tex *dp.Tex
tileSize float64
CharPadding geo.Vec2
Space, Tab, Line float64
chars []Character
}
type jsonChar struct { type jsonChar struct {
Char string Char string
X, Y, Offset float64 X, Y, Offset float64
} }
// Font represents a texture mapped font. // Creates a new font from texture. Characters must be added afterwards.
// It can be loaded from JSON together with a texture. // The characters must be placed within a grid,
type Font struct { // the second parameter describes the width and height of one tile in pixel.
Tex *Tex func NewFont(tex *dp.Tex, tileSize int) *Font {
tileSize float64 font := &Font{}
CharPadding Vec2
Space, Tab, Line float64
chars []character
}
// 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.Tex = tex
font.tileSize = tileSize font.tileSize = float64(tileSize)
font.CharPadding = Vec2{0.05, 0.05} font.CharPadding = geo.Vec2{0.05, 0.05}
font.Space = 0.3 font.Space = 0.3
font.Tab = 1.2 font.Tab = 1.2
font.Line = 1 font.Line = 1
font.chars = make([]character, 0) font.chars = make([]Character, 0)
return &font return font
} }
// Loads characters from JSON file. // Loads characters from JSON file.
@@ -64,7 +82,7 @@ func NewFont(tex *Tex, tileSize float64) *Font {
// Where x and y start in the upper left corner of the texture, both of type int. // 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). // 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. // If cut is set to true, the characters will be true typed.
func (f *Font) FromJson(path string, cut bool) error { func (f *Font) LoadFromJson(path string, cut bool) error {
// load file content // load file content
content, err := ioutil.ReadFile(path) content, err := ioutil.ReadFile(path)
@@ -90,22 +108,21 @@ func (f *Font) extractChars(chars []jsonChar, cut bool) {
continue continue
} }
var min, max, size Vec2 var min, max, size geo.Vec2
if !cut { if !cut {
min = Vec2{char.X * f.tileSize, char.Y * f.tileSize} min = geo.Vec2{char.X * f.tileSize, char.Y * f.tileSize}
max = Vec2{min.X + f.tileSize, min.Y + f.tileSize} max = geo.Vec2{min.X + f.tileSize, min.Y + f.tileSize}
size = Vec2{1, 1} size = geo.Vec2{1, 1}
} else { } else {
min, max, size = f.cutChar(int(char.X), int(char.Y)) 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) (Vec2, Vec2, Vec2) { func (f *Font) cutChar(x, y int) (geo.Vec2, geo.Vec2, geo.Vec2) {
// find min/max corners of character on texture
minX := int(f.Tex.GetSize().X) minX := int(f.Tex.GetSize().X)
minY := int(f.Tex.GetSize().Y) minY := int(f.Tex.GetSize().Y)
maxX := 0 maxX := 0
@@ -134,21 +151,22 @@ func (f *Font) cutChar(x, y int) (Vec2, Vec2, Vec2) {
} }
} }
// add padding
minX -= char_padding minX -= char_padding
maxX += char_padding maxX += char_padding
minY -= char_padding minY -= char_padding
maxY += char_padding maxY += char_padding
texSize := f.Tex.GetSize() texSize := f.Tex.GetSize()
min := Vec2{float64(minX) / texSize.X, float64(maxY) / texSize.Y} min := geo.Vec2{float64(minX) / texSize.X, float64(maxY) / texSize.Y}
max := Vec2{float64(maxX) / texSize.X, float64(minY) / texSize.Y} max := geo.Vec2{float64(maxX) / texSize.X, float64(minY) / texSize.Y}
size := Vec2{float64(maxX-minX) / f.tileSize, float64(maxY-minY) / f.tileSize}
// size
size := geo.Vec2{float64(maxX-minX) / f.tileSize, float64(maxY-minY) / f.tileSize}
return min, max, size return min, max, size
} }
func (f *Font) getChar(char byte) *character { func (f *Font) getChar(char byte) *Character {
for _, character := range f.chars { for _, character := range f.chars {
if character.char == char { if character.char == char {
return &character return &character
@@ -158,55 +176,30 @@ func (f *Font) getChar(char byte) *character {
return nil return nil
} }
// Renderable text component. type Text struct {
// Use together with Text and create using NewText(). *Actor
type TextComponent struct { *Pos2D
Color Vec4
id int
text string text string
bounds Vec2 bounds geo.Vec2
index, vertex, texCoord *VBO
vao *VAO index, vertex, texCoord *dp.VBO
vao *dp.VAO
} }
// Deletes GL buffers bound to this text component. // Deletes GL buffers bound to this text.
func (t *TextComponent) Drop() { func (t *Text) Drop() {
t.index.Drop() t.index.Drop()
t.vertex.Drop() t.vertex.Drop()
t.texCoord.Drop() t.texCoord.Drop()
t.vao.Drop() t.vao.Drop()
} }
// 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
}
// 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
}
// Sets the given string as text and (re)creates buffers. // Sets the given string as text and (re)creates buffers.
func (t *Text) SetText(font *Font, text string) { func (t *Text) SetText(font *Font, text string) {
t.text = text t.text = text
indices := make([]uint32, len(text)*6) indices := make([]uint32, len(text)*6)
vertices := make([]float32, len(text)*8) vertices := make([]float32, len(text)*8)
texCoords := make([]float32, len(text)*8) texCoords := make([]float32, len(text)*8)
@@ -228,7 +221,7 @@ func (t *Text) SetText(font *Font, text string) {
// create vertices/texCoords // create vertices/texCoords
index = 0 index = 0
offset := Vec2{} offset := geo.Vec2{}
var width, height float64 var width, height float64
for i := 0; i < len(text)*8 && int(index) < len(text); i += 8 { for i := 0; i < len(text)*8 && int(index) < len(text); i += 8 {
@@ -292,14 +285,18 @@ func (t *Text) SetText(font *Font, text string) {
} }
} }
t.bounds = Vec2{width, height} t.bounds = geo.Vec2{width, height}
// fill GL buffer // fill GL buffer
t.index.Fill(gl.Ptr(indices[:chars*6]), 4, chars*6, gl.STATIC_DRAW) 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.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) t.texCoord.Fill(gl.Ptr(texCoords[:chars*8]), 4, chars*8, gl.STATIC_DRAW)
CheckGLError() util.CheckGLError()
}
func (t *Text) GetId() int {
return t.id
} }
// Returns the text as string. // Returns the text as string.
@@ -308,130 +305,98 @@ func (t *Text) GetText() string {
} }
// Returns bounds of text, which is the size of characters. // Returns bounds of text, which is the size of characters.
func (t *Text) GetBounds() Vec2 { func (t *Text) GetBounds() geo.Vec2 {
return Vec2{t.bounds.X * t.Size.X * t.Scale.X, t.bounds.Y * t.Size.Y * t.Scale.Y} return geo.Vec2{t.bounds.X * t.Size.X * t.Scale.X, t.bounds.Y * t.Size.Y * t.Scale.Y}
} }
// 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 { type TextRenderer struct {
Pos2D Pos2D
Shader *Shader Shader *dp.Shader
Camera *Camera Camera *Camera
Font *Font Font *Font
texts []Text Color geo.Vec4
texts []*Text
} }
// Creates a new text renderer using given shader, camera and font. // Creates a new text renderer using given shader, camera and font.
// If shader and/or camera are nil, the default one will be used. // If shader and/or camera are nil, the default one will be used.
func NewTextRenderer(shader *Shader, camera *Camera, font *Font) *TextRenderer { func NewTextRenderer(shader *dp.Shader, camera *Camera, font *Font) *TextRenderer {
if shader == nil {
shader = DefaultTextShader
}
if camera == nil {
camera = DefaultCamera
}
renderer := &TextRenderer{} renderer := &TextRenderer{}
renderer.Shader = shader renderer.Shader = shader
renderer.Camera = camera renderer.Camera = camera
renderer.Font = font renderer.Font = font
renderer.texts = make([]Text, 0) renderer.Color = geo.Vec4{1, 1, 1, 1}
renderer.Size = Vec2{1, 1} renderer.texts = make([]*Text, 0)
renderer.Scale = Vec2{1, 1} renderer.Size = geo.Vec2{1, 1}
renderer.Scale = geo.Vec2{1, 1}
return renderer return renderer
} }
// Prepares given text for rendering. // Prepares a text for rendering.
func (r *TextRenderer) Prepare(text *Text) { func (r *TextRenderer) Prepare(text *Text) {
text.vao = NewVAO() text.vao = dp.NewVAO()
text.vao.Bind() text.vao.Bind()
r.Shader.EnableVertexAttribArrays() r.Shader.EnableVertexAttribArrays()
text.index.Bind() text.index.Bind()
text.vertex.Bind() text.vertex.Bind()
text.vertex.AttribPointer(r.Shader.GetAttribLocation(Default_shader_text_vertex_attrib), 2, gl.FLOAT, false, 0) text.vertex.AttribPointer(r.Shader.GetAttribLocation(TEXTRENDERER_VERTEX_ATTRIB), 2, gl.FLOAT, false, 0)
text.texCoord.Bind() text.texCoord.Bind()
text.texCoord.AttribPointer(r.Shader.GetAttribLocation(Default_shader_text_texcoord_attrib), 2, gl.FLOAT, false, 0) text.texCoord.AttribPointer(r.Shader.GetAttribLocation(TEXTRENDERER_TEXCOORD_ATTRIB), 2, gl.FLOAT, false, 0)
text.vao.Unbind() text.vao.Unbind()
} }
// 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()
}
}
// Adds text to the renderer. // Adds text to the renderer.
func (r *TextRenderer) Add(actor *Actor, pos *Pos2D, text *TextComponent) bool { func (r *TextRenderer) Add(text *Text) {
id := actor.GetId() r.texts = append(r.texts, text)
for _, text := range r.texts {
if id == text.Actor.GetId() {
return false
}
}
r.texts = append(r.texts, Text{actor, pos, text})
return true
} }
// Removes text from renderer. // Returns text by ID.
func (r *TextRenderer) Remove(actor *Actor) bool { func (r *TextRenderer) Get(id int) *Text {
return r.RemoveById(actor.GetId()) for _, text := range r.texts {
if text.GetId() == id {
return text
}
}
return nil
} }
// Removes text from renderer by ID. // Removes text from renderer by ID.
func (r *TextRenderer) RemoveById(id ActorId) bool { func (r *TextRenderer) Remove(id int) *Text {
for i, text := range r.texts { for i, text := range r.texts {
if text.Actor.GetId() == id { if text.GetId() == id {
r.texts = append(r.texts[:i], r.texts[i+1:]...) r.texts = append(r.texts[:i], r.texts[i+1:]...)
return true return text
} }
} }
return false return nil
} }
// Removes all texts. // Removes all sprites.
func (r *TextRenderer) RemoveAll() { func (r *TextRenderer) Clear() {
r.texts = make([]Text, 0) r.texts = make([]*Text, 0)
} }
// Returns number of texts. // Renders sprites.
func (r *TextRenderer) Len() int { func (r *TextRenderer) Render() {
return len(r.texts)
}
func (r *TextRenderer) GetName() string {
return text_renderer_name
}
// Renders texts.
func (r *TextRenderer) Update(delta float64) {
if r.Font == nil {
return
}
r.Shader.Bind() r.Shader.Bind()
r.Shader.SendMat3(Default_shader_text_ortho, *MultMat3(r.Camera.CalcOrtho(), r.CalcModel())) r.Shader.SendMat3(TEXTRENDERER_ORTHO, *geo.MultMat3(r.Camera.CalcOrtho(), r.CalcModel()))
r.Shader.SendUniform1i(Default_shader_text_tex, 0) 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.Font.Tex.Bind() r.Font.Tex.Bind()
for _, text := range r.texts { for i := range r.texts {
if !text.Visible { if !r.texts[i].Visible {
continue continue
} }
text.vao.Bind() r.texts[i].vao.Bind()
r.Shader.SendUniform4f(Default_shader_text_color, float32(text.Color.X), float32(text.Color.Y), float32(text.Color.Z), float32(text.Color.W)) r.Shader.SendMat3(TEXTRENDERER_MODEL, *r.texts[i].CalcModel())
r.Shader.SendMat3(Default_shader_text_model, *text.CalcModel())
gl.DrawElements(gl.TRIANGLES, text.index.Size(), gl.UNSIGNED_INT, nil) gl.DrawElements(gl.TRIANGLES, r.texts[i].index.Size(), gl.UNSIGNED_INT, nil)
} }
} }
*/

2
vao.go
View File

@@ -1,7 +1,7 @@
package goga package goga
import ( import (
"github.com/go-gl/gl/v3.2-core/gl" "github.com/go-gl/gl/v4.5-core/gl"
) )
// Vertex Array Object. // Vertex Array Object.

2
vbo.go
View File

@@ -1,7 +1,7 @@
package goga package goga
import ( import (
"github.com/go-gl/gl/v3.2-core/gl" "github.com/go-gl/gl/v4.5-core/gl"
"unsafe" "unsafe"
) )