8 Commits

Author SHA1 Message Date
Marvin Blum
e88c163725 More code restructuring and system access functions. 2016-08-19 00:15:27 +02:00
Marvin Blum
bc624295be Simplified for loops in render systems. 2016-08-19 00:04:05 +02:00
Marvin Blum
69886eff00 Code restructuring. 2016-08-18 23:58:41 +02:00
Marvin Blum
6ffa5aa431 Removed unnecessary true. 2016-08-14 13:31:43 +02:00
Marvin Blum
25814cf022 Set GL version to 3.1 (lowest possible version), fixed cleanup bug when calling Stop(), added input handling. 2016-08-14 13:20:01 +02:00
Marvin Blum
092ad543e5 Fixed bug in text renderer, upgraded to GLFW 3.2, added fullscreen and FPS limitation. 2016-08-08 21:35:01 +02:00
Marvin Blum
3c1fb484b9 Added texture mapped font rendering. 2016-08-07 00:32:16 +02:00
Marvin Blum
ed39081300 Moved types to top of files, started 2D texture mapped font rendering. 2016-07-16 02:31:53 +02:00
31 changed files with 802 additions and 341 deletions

10
CHANGELOG.md Normal file
View File

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

View File

@@ -1,15 +1,14 @@
# 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/v4.5-core/gl go get github.com/go-gl/gl/v3.2-core/gl
go get github.com/go-gl/glfw/v3.1/glfw go get github.com/go-gl/glfw/v3.2/glfw
go get github.com/DeKugelschieber/go-game go get github.com/DeKugelschieber/go-game
``` ```
@@ -17,18 +16,16 @@ 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
- 4.5-core - 3.1-core
* https://github.com/go-gl/glfw * https://github.com/go-gl/glfw
- 3.1 - 3.1
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. To use a different GL version, you need to replace the GL imports in package goga.
## Contribute ## Contribute

View File

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

View File

@@ -1,6 +1,8 @@
package goga package goga
import () var (
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.
@@ -12,10 +14,6 @@ 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 i := range c.cullables { for _, cullable := range c.cullables {
if c.cullables[i].Pos.X > c.viewport.Z || if cullable.Pos.X > c.viewport.Z ||
c.cullables[i].Pos.X+c.cullables[i].Size.X < c.viewport.X || cullable.Pos.X+cullable.Size.X < c.viewport.X ||
c.cullables[i].Pos.Y > c.viewport.W || cullable.Pos.Y > c.viewport.W ||
c.cullables[i].Pos.Y+c.cullables[i].Size.Y < c.viewport.Y { cullable.Pos.Y+cullable.Size.Y < c.viewport.Y {
c.cullables[i].Visible = false cullable.Visible = false
} else { } else {
c.cullables[i].Visible = true cullable.Visible = true
} }
} }
} }

83
default_shader.go Normal file
View File

@@ -0,0 +1,83 @@
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.

After

Width:  |  Height:  |  Size: 40 KiB

77
demo/input/input.go Normal file
View File

@@ -0,0 +1,77 @@
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,12 +39,7 @@ 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, ok := goga.GetSystemByName("keyframeRenderer").(*goga.KeyframeRenderer) renderer := goga.GetKeyframeRenderer()
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)
} }
@@ -55,6 +50,7 @@ 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,12 +36,7 @@ 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, ok := goga.GetSystemByName("modelRenderer").(*goga.ModelRenderer) renderer := goga.GetModelRenderer()
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,20 +26,10 @@ func (g *Game) Setup() {
} }
sprite := goga.NewSprite(tex) sprite := goga.NewSprite(tex)
renderer, ok := goga.GetSystemByName("spriteRenderer").(*goga.SpriteRenderer) renderer := goga.GetSpriteRenderer()
if !ok {
panic("Could not find renderer")
}
renderer.Add(sprite.Actor, sprite.Pos2D, sprite.Tex) renderer.Add(sprite.Actor, sprite.Pos2D, sprite.Tex)
culling, ok := goga.GetSystemByName("culling2d").(*goga.Culling2D) culling := goga.GetCulling2DSystem()
if !ok {
panic("Could not find culling")
}
culling.Add(sprite.Actor, sprite.Pos2D) culling.Add(sprite.Actor, sprite.Pos2D)
} }

View File

@@ -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}
]

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

68
demo/text/text.go Normal file
View File

@@ -0,0 +1,68 @@
package main
import (
"github.com/DeKugelschieber/go-game"
"github.com/go-gl/gl/v3.2-core/gl"
)
const (
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
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)
}
pngLoader.KeepData = false
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 main() {
game := Game{}
options := goga.RunOptions{ClearColor: goga.Vec4{0, 0, 0, 0},
Resizable: true,
SetViewportOnResize: true,
ExitOnClose: true}
goga.Run(&game, &options)
}

View File

@@ -7,7 +7,7 @@ type Dropable interface {
} }
// Drops given GL objects. // Drops given GL objects.
// Objects must implement the Dropable inteface. // Objects must implement the Dropable interface.
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/v4.5-core/gl" "github.com/go-gl/gl/v3.2-core/gl"
) )
// Frame Buffer Object. // Frame Buffer Object.

179
game.go
View File

@@ -1,8 +1,8 @@
package goga package goga
import ( import (
"github.com/go-gl/gl/v4.5-core/gl" "github.com/go-gl/gl/v3.2-core/gl"
"github.com/go-gl/glfw/v3.1/glfw" "github.com/go-gl/glfw/v3.2/glfw"
"log" "log"
"math" "math"
"runtime" "runtime"
@@ -14,112 +14,8 @@ 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
// 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;
}`
) )
// If set in RunOptions, the function will be called on window resize.
type ResizeCallback func(width, height int)
// Run options allow to set some parameters on startup.
type RunOptions struct {
Title string
Width uint32
Height uint32
ClearColor Vec4
Resizable bool
SetViewportOnResize bool
ResizeCallbackFunc ResizeCallback
ExitOnClose bool
}
// Main game object.
// Setup will be called before the main loop and after GL context has been created.
// Update will be called each frame. This can be used to switch scenes or end game on win state.
// For game logic, System should be used.
type Game interface {
Setup()
Update(float64)
}
var ( var (
running = true running = true
clearColor = Vec4{} clearColor = Vec4{}
@@ -134,8 +30,36 @@ var (
DefaultTextShader *Shader DefaultTextShader *Shader
) )
// If set in RunOptions, the function will be called on window resize.
type ResizeCallback func(width, height int)
// Run options allow to set some parameters on startup.
type RunOptions struct {
Title string
Width uint32
Height uint32
ClearColor Vec4
Resizable bool
SetViewportOnResize bool
ResizeCallbackFunc ResizeCallback
ExitOnClose bool
RefreshRate int
Fullscreen bool
MonitorId uint // index
}
// Main game object.
// Setup will be called before the main loop and after GL context has been created.
// Update will be called each frame. This can be used to switch scenes or end game on win state.
// For game logic, System should be used.
type Game interface {
Setup()
Update(float64)
}
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()
} }
@@ -168,32 +92,52 @@ 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 && options.Width > 0 { if options != nil {
if options.Width > 0 {
width = options.Width width = options.Width
} }
if options != nil && options.Height > 0 { if options.Height > 0 {
height = options.Height height = options.Height
} }
if options != nil && len(options.Title) > 0 { if len(options.Title) > 0 {
title = options.Title title = options.Title
} }
if options != nil {
exitOnClose = options.ExitOnClose exitOnClose = options.ExitOnClose
}
if options != nil && !options.Resizable { if !options.Resizable {
glfw.WindowHint(glfw.Resizable, glfw.False) glfw.WindowHint(glfw.Resizable, glfw.False)
} }
}
wnd, err := glfw.CreateWindow(int(width), int(height), title, nil, nil) var monitor *glfw.Monitor
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 {
@@ -211,6 +155,8 @@ func Run(game Game, options *RunOptions) {
} }
}) })
initInput(wnd)
// make GL context current // make GL context current
wnd.MakeContextCurrent() wnd.MakeContextCurrent()
@@ -235,8 +181,8 @@ func Run(game Game, options *RunOptions) {
delta := time.Duration(0) delta := time.Duration(0)
var deltaSec float64 var deltaSec float64
for running { for {
if exitOnClose && wnd.ShouldClose() { if !running || exitOnClose && wnd.ShouldClose() {
cleanup() cleanup()
return return
} }
@@ -310,6 +256,7 @@ 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/v4.5-core/gl" "github.com/go-gl/gl/v3.2-core/gl"
"log" "log"
) )

98
input.go Normal file
View File

@@ -0,0 +1,98 @@
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/v4.5-core/gl" "github.com/go-gl/gl/v3.2-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 i := range s.sprites { for _, sprite := range s.sprites {
if s.sprites[i].KeyframeAnimation == nil { if sprite.KeyframeAnimation == nil {
continue continue
} }
s.sprites[i].Interpolation += delta * s.sprites[i].KeyframeAnimation.Speed sprite.Interpolation += delta * sprite.KeyframeAnimation.Speed
if s.sprites[i].Interpolation > 1 { if sprite.Interpolation > 1 {
s.sprites[i].Interpolation = 0 sprite.Interpolation = 0
s.sprites[i].Current++ sprite.Current++
if s.sprites[i].Current > s.sprites[i].KeyframeAnimation.End { if sprite.Current > sprite.KeyframeAnimation.End {
if s.sprites[i].KeyframeAnimation.Loop { if sprite.KeyframeAnimation.Loop {
s.sprites[i].Current = s.sprites[i].KeyframeAnimation.Start sprite.Current = sprite.KeyframeAnimation.Start
} else { } else {
s.sprites[i].Current = s.sprites[i].KeyframeAnimation.End sprite.Current = sprite.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 i := range s.sprites { for _, sprite := range s.sprites {
if !s.sprites[i].Visible { if !sprite.Visible {
continue continue
} }
texCoord := s.sprites[i].KeyframeSet.Keyframes[s.sprites[i].Current].texCoord texCoord := sprite.KeyframeSet.Keyframes[sprite.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, *s.sprites[i].CalcModel()) s.Shader.SendMat3(Default_shader_2D_model, *sprite.CalcModel())
s.sprites[i].Tex.Bind() sprite.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/v4.5-core/gl" "github.com/go-gl/gl/v3.2-core/gl"
"image" "image"
"image/draw" "image/draw"
"image/png" "image/png"
@@ -75,6 +75,13 @@ 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 {
@@ -124,13 +131,6 @@ 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/v4.5-core/gl" "github.com/go-gl/gl/v3.2-core/gl"
) )
const ( const (
@@ -174,20 +174,20 @@ func (s *ModelRenderer) Update(delta float64) {
var tid uint32 var tid uint32
for i := range s.models { for _, model := range s.models {
if !s.models[i].Visible { if !model.Visible {
continue continue
} }
s.Shader.SendMat4(Default_shader_3D_model, *s.models[i].CalcModel()) s.Shader.SendMat4(Default_shader_3D_model, *model.CalcModel())
s.models[i].Vao.Bind() model.Vao.Bind()
// prevent texture switching when not neccessary // prevent texture switching when not neccessary
if tid != s.models[i].Tex.GetId() { if tid != model.Tex.GetId() {
tid = s.models[i].Tex.GetId() tid = model.Tex.GetId()
s.models[i].Tex.Bind() model.Tex.Bind()
} }
gl.DrawElements(gl.TRIANGLES, s.models[i].Index.Size(), gl.UNSIGNED_INT, nil) gl.DrawElements(gl.TRIANGLES, model.Index.Size(), gl.UNSIGNED_INT, nil)
} }
} }

View File

@@ -4,6 +4,11 @@ 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
@@ -19,11 +24,6 @@ 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/v4.5-core/gl" "github.com/go-gl/gl/v3.2-core/gl"
"log" "log"
"strings" "strings"
) )

View File

@@ -1,7 +1,7 @@
package goga package goga
import ( import (
"github.com/go-gl/gl/v4.5-core/gl" "github.com/go-gl/gl/v3.2-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 i := range s.sprites { for _, sprite := range s.sprites {
if !s.sprites[i].Visible { if !sprite.Visible {
continue continue
} }
s.Shader.SendMat3(Default_shader_2D_model, *s.sprites[i].CalcModel()) s.Shader.SendMat3(Default_shader_2D_model, *sprite.CalcModel())
// prevent texture switching when not neccessary // prevent texture switching when not neccessary
if tid != s.sprites[i].Tex.GetId() { if tid != sprite.Tex.GetId() {
tid = s.sprites[i].Tex.GetId() tid = sprite.Tex.GetId()
s.sprites[i].Tex.Bind() sprite.Tex.Bind()
} }
gl.DrawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, nil) gl.DrawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, nil)

View File

@@ -2,6 +2,10 @@ 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.
@@ -17,10 +21,6 @@ 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 {

51
system_util.go Normal file
View File

@@ -0,0 +1,51 @@
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/v4.5-core/gl" "github.com/go-gl/gl/v3.2-core/gl"
"image" "image"
) )

259
text.go
View File

@@ -1,69 +1,51 @@
package goga package goga
/*
import ( import (
"core"
"dp"
"encoding/json" "encoding/json"
"geo" "github.com/go-gl/gl/v3.2-core/gl"
"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"
) )
// Returns a new renderable text object. type character struct {
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 geo.Vec2 min, max, size 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
} }
// Creates a new font from texture. Characters must be added afterwards. // Font represents a texture mapped font.
// The characters must be placed within a grid, // It can be loaded from JSON together with a texture.
// the second parameter describes the width and height of one tile in pixel. type Font struct {
func NewFont(tex *dp.Tex, tileSize int) *Font { Tex *Tex
font := &Font{} tileSize float64
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 = float64(tileSize) font.tileSize = tileSize
font.CharPadding = geo.Vec2{0.05, 0.05} font.CharPadding = 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.
@@ -82,7 +64,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. // 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) LoadFromJson(path string, cut bool) error { func (f *Font) FromJson(path string, cut bool) error {
// load file content // load file content
content, err := ioutil.ReadFile(path) content, err := ioutil.ReadFile(path)
@@ -108,21 +90,22 @@ func (f *Font) extractChars(chars []jsonChar, cut bool) {
continue continue
} }
var min, max, size geo.Vec2 var min, max, size Vec2
if !cut { if !cut {
min = geo.Vec2{char.X * f.tileSize, char.Y * f.tileSize} min = Vec2{char.X * f.tileSize, char.Y * f.tileSize}
max = geo.Vec2{min.X + f.tileSize, min.Y + f.tileSize} max = Vec2{min.X + f.tileSize, min.Y + f.tileSize}
size = geo.Vec2{1, 1} size = 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) (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) minX := int(f.Tex.GetSize().X)
minY := int(f.Tex.GetSize().Y) minY := int(f.Tex.GetSize().Y)
maxX := 0 maxX := 0
@@ -151,22 +134,21 @@ func (f *Font) cutChar(x, y int) (geo.Vec2, geo.Vec2, geo.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 := geo.Vec2{float64(minX) / texSize.X, float64(maxY) / texSize.Y} min := Vec2{float64(minX) / texSize.X, float64(maxY) / texSize.Y}
max := geo.Vec2{float64(maxX) / texSize.X, float64(minY) / texSize.Y} max := 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
@@ -176,30 +158,55 @@ func (f *Font) getChar(char byte) *Character {
return nil return nil
} }
type Text struct { // Renderable text component.
*Actor // Use together with Text and create using NewText().
*Pos2D type TextComponent struct {
Color Vec4
id int
text string text string
bounds geo.Vec2 bounds Vec2
index, vertex, texCoord *VBO
index, vertex, texCoord *dp.VBO vao *VAO
vao *dp.VAO
} }
// Deletes GL buffers bound to this text. // Deletes GL buffers bound to this text component.
func (t *Text) Drop() { func (t *TextComponent) 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)
@@ -221,7 +228,7 @@ func (t *Text) SetText(font *Font, text string) {
// create vertices/texCoords // create vertices/texCoords
index = 0 index = 0
offset := geo.Vec2{} offset := 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 {
@@ -285,18 +292,14 @@ func (t *Text) SetText(font *Font, text string) {
} }
} }
t.bounds = geo.Vec2{width, height} t.bounds = 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)
util.CheckGLError() CheckGLError()
}
func (t *Text) GetId() int {
return t.id
} }
// Returns the text as string. // Returns the text as string.
@@ -305,98 +308,130 @@ 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() geo.Vec2 { func (t *Text) GetBounds() Vec2 {
return geo.Vec2{t.bounds.X * t.Size.X * t.Scale.X, t.bounds.Y * t.Size.Y * t.Scale.Y} return 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 *dp.Shader Shader *Shader
Camera *Camera Camera *Camera
Font *Font Font *Font
Color geo.Vec4 texts []Text
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 *dp.Shader, camera *Camera, font *Font) *TextRenderer { func NewTextRenderer(shader *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.Color = geo.Vec4{1, 1, 1, 1} renderer.texts = make([]Text, 0)
renderer.texts = make([]*Text, 0) renderer.Size = Vec2{1, 1}
renderer.Size = geo.Vec2{1, 1} renderer.Scale = Vec2{1, 1}
renderer.Scale = geo.Vec2{1, 1}
return renderer return renderer
} }
// Prepares a text for rendering. // Prepares given text for rendering.
func (r *TextRenderer) Prepare(text *Text) { func (r *TextRenderer) Prepare(text *Text) {
text.vao = dp.NewVAO() text.vao = 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(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.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() text.vao.Unbind()
} }
// Adds text to the renderer. // Frees recources created by text component.
func (r *TextRenderer) Add(text *Text) { // This is called automatically when system gets removed.
r.texts = append(r.texts, text) func (r *TextRenderer) Cleanup() {
for _, text := range r.texts {
text.Drop()
}
} }
// Returns text by ID. // Adds text to the renderer.
func (r *TextRenderer) Get(id int) *Text { func (r *TextRenderer) Add(actor *Actor, pos *Pos2D, text *TextComponent) bool {
id := actor.GetId()
for _, text := range r.texts { for _, text := range r.texts {
if text.GetId() == id { if id == text.Actor.GetId() {
return text 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. // 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 { 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:]...) r.texts = append(r.texts[:i], r.texts[i+1:]...)
return text return true
} }
} }
return nil return false
} }
// Removes all sprites. // Removes all texts.
func (r *TextRenderer) Clear() { func (r *TextRenderer) RemoveAll() {
r.texts = make([]*Text, 0) r.texts = make([]Text, 0)
} }
// Renders sprites. // Returns number of texts.
func (r *TextRenderer) Render() { 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) {
if r.Font == nil {
return
}
r.Shader.Bind() r.Shader.Bind()
r.Shader.SendMat3(TEXTRENDERER_ORTHO, *geo.MultMat3(r.Camera.CalcOrtho(), r.CalcModel())) r.Shader.SendMat3(Default_shader_text_ortho, *MultMat3(r.Camera.CalcOrtho(), r.CalcModel()))
r.Shader.SendUniform1i(TEXTRENDERER_TEX, 0) r.Shader.SendUniform1i(Default_shader_text_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 i := range r.texts { for _, text := range r.texts {
if !r.texts[i].Visible { if !text.Visible {
continue continue
} }
r.texts[i].vao.Bind() text.vao.Bind()
r.Shader.SendMat3(TEXTRENDERER_MODEL, *r.texts[i].CalcModel()) 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(Default_shader_text_model, *text.CalcModel())
gl.DrawElements(gl.TRIANGLES, r.texts[i].index.Size(), gl.UNSIGNED_INT, nil) gl.DrawElements(gl.TRIANGLES, text.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/v4.5-core/gl" "github.com/go-gl/gl/v3.2-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/v4.5-core/gl" "github.com/go-gl/gl/v3.2-core/gl"
"unsafe" "unsafe"
) )