1 Commits

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

View File

@@ -1,5 +0,0 @@
# Changelog
## 0.1_beta
* beta release

View File

@@ -1,13 +1,14 @@
# 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.
**Under heavy development, do not use yet!**
## Install
go-game requires OpenGL and GLFW. The following three steps install everything you need:
```
go get github.com/go-gl/gl/v3.1-core/gl
go get github.com/go-gl/gl/v4.5-core/gl
go get github.com/go-gl/glfw/v3.1/glfw
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
*WIP*
Examples can be found within the demo folder. For full reference visit: https://godoc.org/github.com/DeKugelschieber/go-game
## Dependencies
* https://github.com/go-gl/gl
- 3.1-core
- 4.5-core
* https://github.com/go-gl/glfw
- 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

View File

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

View File

@@ -1,5 +1,7 @@
package goga
import ()
// An actor ID is a unique integer,
// which can be used to reference an actor.
type ActorId uint64

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,87 +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, ok := goga.GetSystemByName("spriteRenderer").(*goga.SpriteRenderer)
if !ok {
panic("Could not find renderer")
}
renderer.Add(sprite.Actor, sprite.Pos2D, sprite.Tex)
culling, ok := goga.GetSystemByName("culling2d").(*goga.Culling2D)
if !ok {
panic("Could not find culling")
}
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

@@ -55,7 +55,6 @@ func main() {
options := goga.RunOptions{ClearColor: goga.Vec4{0, 0, 0, 0},
Resizable: true,
SetViewportOnResize: true,
ExitOnClose: true,
Fullscreen: true}
ExitOnClose: true}
goga.Run(&game, &options)
}

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,70 +2,52 @@ 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"
gopher_path = "src/github.com/DeKugelschieber/go-game/demo/sprite/assets/gopher.png"
)
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)
_, err := goga.LoadRes(gopher_path)
if err != nil {
panic(err)
}
pngLoader.KeepData = false
pngLoader.Filter = gl.LINEAR
// create font
tex, err := goga.GetTex("victor.png")
// create sprite
tex, err := goga.GetTex("gopher.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, ok := goga.GetSystemByName("textRenderer").(*goga.TextRenderer)
sprite := goga.NewSprite(tex)
renderer, ok := goga.GetSystemByName("spriteRenderer").(*goga.SpriteRenderer)
if !ok {
panic("Could not find renderer")
}
renderer.Font = font
renderer.Add(sprite.Actor, sprite.Pos2D, sprite.Tex)
// 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)
culling, ok := goga.GetSystemByName("culling2d").(*goga.Culling2D)
if !ok {
panic("Could not find culling")
}
culling.Add(sprite.Actor, sprite.Pos2D)
}
func (g *Game) Update(delta float64) {}
func main() {
game := Game{}
options := goga.RunOptions{ClearColor: goga.Vec4{0, 0, 0, 0},
options := goga.RunOptions{ClearColor: goga.Vec4{1, 1, 1, 0},
Resizable: true,
SetViewportOnResize: true,
ExitOnClose: true}

View File

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

2
fbo.go
View File

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

131
game.go
View File

@@ -1,8 +1,8 @@
package goga
import (
"github.com/go-gl/gl/v3.2-core/gl"
"github.com/go-gl/glfw/v3.2/glfw"
"github.com/go-gl/gl/v4.5-core/gl"
"github.com/go-gl/glfw/v3.1/glfw"
"log"
"math"
"runtime"
@@ -14,6 +14,86 @@ const (
default_height = uint32(400)
default_title = "Game"
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.
@@ -29,9 +109,6 @@ type RunOptions struct {
SetViewportOnResize bool
ResizeCallbackFunc ResizeCallback
ExitOnClose bool
RefreshRate int
Fullscreen bool
MonitorId uint // index
}
// Main game object.
@@ -58,8 +135,7 @@ var (
)
func init() {
// GL functions must be called from main thread,
// so we disable multithreading by the runtime here.
// GL functions must be called from main thread.
log.Print("Locking OS thread")
runtime.LockOSThread()
}
@@ -92,52 +168,32 @@ func Run(game Game, options *RunOptions) {
title := default_title
exitOnClose := default_exit_on_close
if options != nil {
if options.Width > 0 {
if options != nil && options.Width > 0 {
width = options.Width
}
if options.Height > 0 {
if options != nil && options.Height > 0 {
height = options.Height
}
if len(options.Title) > 0 {
if options != nil && len(options.Title) > 0 {
title = options.Title
}
if options != nil {
exitOnClose = options.ExitOnClose
}
if !options.Resizable {
if options != nil && !options.Resizable {
glfw.WindowHint(glfw.Resizable, glfw.False)
}
}
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)
wnd, err := glfw.CreateWindow(int(width), int(height), title, nil, nil)
if err != nil {
panic("Error creating GLFW window: " + err.Error())
}
if options != nil && options.RefreshRate != 0 {
glfw.WindowHint(glfw.RefreshRate, options.RefreshRate)
}
// window event handlers
wnd.SetSizeCallback(func(w *glfw.Window, width, height int) {
if options == nil {
@@ -155,8 +211,6 @@ func Run(game Game, options *RunOptions) {
}
})
initInput(wnd)
// make GL context current
wnd.MakeContextCurrent()
@@ -181,8 +235,8 @@ func Run(game Game, options *RunOptions) {
delta := time.Duration(0)
var deltaSec float64
for {
if !running || exitOnClose && wnd.ShouldClose() {
for running {
if exitOnClose && wnd.ShouldClose() {
cleanup()
return
}
@@ -256,7 +310,6 @@ func initGoga(width, height int) {
AddSystem(NewModelRenderer(nil, nil, false))
AddSystem(NewCulling2D(0, 0, width, height))
AddSystem(NewKeyframeRenderer(nil, nil))
AddSystem(NewTextRenderer(nil, nil, nil)) // font must be set outside!
}
func cleanup() {

View File

@@ -1,7 +1,7 @@
package goga
import (
"github.com/go-gl/gl/v3.2-core/gl"
"github.com/go-gl/gl/v4.5-core/gl"
"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
import (
"github.com/go-gl/gl/v3.2-core/gl"
"github.com/go-gl/gl/v4.5-core/gl"
)
const (
@@ -14,44 +14,6 @@ type Keyframe struct {
Min, Max Vec2
}
// A set of keyframes making up an animation.
type KeyframeSet struct {
Keyframes []Keyframe
}
// Keyframe animation component.
// It has a start and an end frame, a play speed and option to loop.
type KeyframeAnimation struct {
Start, End int
Loop bool
Speed float64
Current int
Interpolation float64
}
// An animated sprite is a sprite with keyframe animation information.
// It will be updated and rendered by the KeyframeRenderer.
type AnimatedSprite struct {
*Actor
*Pos2D
*Tex
*KeyframeSet
*KeyframeAnimation
}
// The keyframe renderer renders animated sprites.
// It has a 2D position component, to move all sprites at once.
type KeyframeRenderer struct {
Pos2D
Shader *Shader
Camera *Camera
sprites []AnimatedSprite
index, vertex *VBO
vao *VAO
}
// Creates a new single keyframe with texture VBO.
func NewKeyframe(min, max Vec2) *Keyframe {
keyframe := &Keyframe{Min: min, Max: max}
@@ -74,6 +36,11 @@ func NewKeyframe(min, max Vec2) *Keyframe {
return keyframe
}
// A set of keyframes making up an animation.
type KeyframeSet struct {
Keyframes []Keyframe
}
// Creates a new empty keyframe set with given size.
func NewKeyframeSet() *KeyframeSet {
set := &KeyframeSet{}
@@ -88,11 +55,31 @@ func (s *KeyframeSet) Add(frame *Keyframe) int {
return len(s.Keyframes)
}
// Keyframe animation component.
// It has a start and an end frame, a play speed and option to loop.
type KeyframeAnimation struct {
Start, End int
Loop bool
Speed float64
Current int
Interpolation float64
}
// Creates a new keyframe animation with given start, end and loop.
func NewKeyframeAnimation(start, end int, loop bool, speed float64) *KeyframeAnimation {
return &KeyframeAnimation{start, end, loop, speed, 0, 0}
}
// An animated sprite is a sprite with keyframe animation information.
// It will be updated and rendered by the KeyframeRenderer.
type AnimatedSprite struct {
*Actor
*Pos2D
*Tex
*KeyframeSet
*KeyframeAnimation
}
// Creates a new animated sprite.
func NewAnimatedSprite(tex *Tex, set *KeyframeSet, width, height int) *AnimatedSprite {
sprite := &AnimatedSprite{}
@@ -114,6 +101,19 @@ func NewAnimatedSprite(tex *Tex, set *KeyframeSet, width, height int) *AnimatedS
return sprite
}
// The keyframe renderer renders animated sprites.
// It has a 2D position component, to move all sprites at once.
type KeyframeRenderer struct {
Pos2D
Shader *Shader
Camera *Camera
sprites []AnimatedSprite
index, vertex *VBO
vao *VAO
}
// Creates a new keyframe renderer using given shader and camera.
// If shader and/or camera are nil, the default one will be used.
func NewKeyframeRenderer(shader *Shader, camera *Camera) *KeyframeRenderer {

View File

@@ -3,7 +3,7 @@ package goga
import (
"bufio"
"errors"
"github.com/go-gl/gl/v3.2-core/gl"
"github.com/go-gl/gl/v4.5-core/gl"
"image"
"image/draw"
"image/png"
@@ -21,27 +21,6 @@ type PngLoader struct {
KeepData bool
}
// Standford ply file resource.
type Ply struct {
name string
path string
ext string
firstLine, data, hasVertex, hasTexCoord, hasNormal bool
elements, faces int
indices []uint32
vertices, texCoords, normals []float32
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
}
func (p *PngLoader) Load(file string) (Res, error) {
// load texture
imgFile, err := os.Open(file)
@@ -82,6 +61,20 @@ func (p *PngLoader) Ext() string {
return "png"
}
// Standford ply file resource.
type Ply struct {
name string
path string
ext string
firstLine, data, hasVertex, hasTexCoord, hasNormal bool
elements, faces int
indices []uint32
vertices, texCoords, normals []float32
IndexBuffer, VertexBuffer, TexCoordBuffer, NormalBuffer *VBO
}
// Drops contained GL buffers.
func (p *Ply) Drop() {
if p.IndexBuffer != nil {
@@ -131,6 +124,13 @@ func (p *Ply) SetExt(ext string) {
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) {
handle, err := os.Open(file)
defer handle.Close()

View File

@@ -1,7 +1,7 @@
package goga
import (
"github.com/go-gl/gl/v3.2-core/gl"
"github.com/go-gl/gl/v4.5-core/gl"
)
const (
@@ -14,26 +14,6 @@ type Mesh struct {
Vao *VAO
}
// Model is an actor having a 3D position, a texture and a 3D mesh.
type Model struct {
*Actor
*Pos3D
*Tex
*Mesh
}
// The model renderer is a system rendering models.
// It has a 3D position component, to move all models at once.
type ModelRenderer struct {
Pos3D
Shader *Shader
Camera *Camera
ortho bool
models []Model
}
// Creates a new mesh with given GL buffers.
// The VAO must be prepared by ModelRenderer.
func NewMesh(index, vertex, texcoord *VBO) *Mesh {
@@ -56,6 +36,14 @@ func (m *Mesh) Drop() {
m.Vao.Drop()
}
// Model is an actor having a 3D position, a texture and a 3D mesh.
type Model struct {
*Actor
*Pos3D
*Tex
*Mesh
}
// Creates a new model with given mesh and texture.
func NewModel(mesh *Mesh, tex *Tex) *Model {
model := &Model{}
@@ -72,6 +60,18 @@ func NewModel(mesh *Mesh, tex *Tex) *Model {
return model
}
// The model renderer is a system rendering models.
// It has a 3D position component, to move all models at once.
type ModelRenderer struct {
Pos3D
Shader *Shader
Camera *Camera
ortho bool
models []Model
}
// Creates a new model renderer using given shader and camera.
// If shader and/or camera are nil, the default one will be used.
// Orth can be set to true, to use orthogonal projection.

14
pos.go
View File

@@ -8,13 +8,6 @@ type Pos2D struct {
M Mat3
}
// Position component for 3D objects
type Pos3D struct {
Pos, Size, Scale, RotPoint, Rot Vec3
Visible bool
M Mat4
}
// Creates a default initialized Pos2D.
func NewPos2D() *Pos2D {
m := Mat3{}
@@ -45,6 +38,13 @@ func (p *Pos2D) PointInRect(point Vec2) bool {
return point.X > p.Pos.X && point.X < p.Pos.X+p.Size.X*p.Scale.X && point.Y > p.Pos.Y && point.Y < p.Pos.Y+p.Size.Y*p.Scale.Y
}
// Position component for 3D objects
type Pos3D struct {
Pos, Size, Scale, RotPoint, Rot Vec3
Visible bool
M Mat4
}
// Creates a default initialized Pos3D.
func NewPos3D() *Pos3D {
m := Mat4{}

View File

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

View File

@@ -1,7 +1,7 @@
package goga
import (
"github.com/go-gl/gl/v3.2-core/gl"
"github.com/go-gl/gl/v4.5-core/gl"
)
const (
@@ -15,19 +15,6 @@ type Sprite struct {
*Tex
}
// The sprite renderer is a system rendering sprites.
// It has a 2D position component, to move all sprites at once.
type SpriteRenderer struct {
Pos2D
Shader *Shader
Camera *Camera
sprites []Sprite
index, vertex, texCoord *VBO
vao *VAO
}
// Creates a new sprite with given texture.
func NewSprite(tex *Tex) *Sprite {
sprite := &Sprite{}
@@ -43,6 +30,19 @@ func NewSprite(tex *Tex) *Sprite {
return sprite
}
// The sprite renderer is a system rendering sprites.
// It has a 2D position component, to move all sprites at once.
type SpriteRenderer struct {
Pos2D
Shader *Shader
Camera *Camera
sprites []Sprite
index, vertex, texCoord *VBO
vao *VAO
}
// Creates a new sprite renderer using given shader and camera.
// If shader and/or camera are nil, the default one will be used.
func NewSpriteRenderer(shader *Shader, camera *Camera, flip bool) *SpriteRenderer {

2
tex.go
View File

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

293
text.go
View File

@@ -1,122 +1,69 @@
package goga
/*
import (
"core"
"dp"
"encoding/json"
"github.com/go-gl/gl/v3.2-core/gl"
"geo"
"github.com/go-gl/gl/v4.5-core/gl"
"io/ioutil"
"util"
)
const (
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
min, max, size Vec2
min, max, size geo.Vec2
offset float64
}
type Font struct {
Tex *dp.Tex
tileSize float64
CharPadding geo.Vec2
Space, Tab, Line float64
chars []Character
}
type jsonChar struct {
Char string
X, Y, Offset float64
}
// Font represents a texture mapped font.
// It can be loaded from JSON together with a texture.
type Font struct {
Tex *Tex
tileSize float64
CharPadding Vec2
Space, Tab, Line float64
chars []character
}
// Renderable text component.
// Use together with Text and create using NewText().
type TextComponent struct {
Color Vec4
text string
bounds Vec2
index, vertex, texCoord *VBO
vao *VAO
}
// Text is an actor representing text rendered as texture mapped font.
// Each Text has a position and its own buffers.
type Text struct {
*Actor
*Pos2D
*TextComponent
}
// The text renderer is a system rendering 2D texture mapped font.
// It has a 2D position component, to move all texts at once.
type TextRenderer struct {
Pos2D
Shader *Shader
Camera *Camera
Font *Font
texts []Text
}
// 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{}
// Creates a new font from texture. Characters must be added afterwards.
// The characters must be placed within a grid,
// the second parameter describes the width and height of one tile in pixel.
func NewFont(tex *dp.Tex, tileSize int) *Font {
font := &Font{}
font.Tex = tex
font.tileSize = tileSize
font.CharPadding = Vec2{0.05, 0.05}
font.tileSize = float64(tileSize)
font.CharPadding = geo.Vec2{0.05, 0.05}
font.Space = 0.3
font.Tab = 1.2
font.Line = 1
font.chars = make([]character, 0)
font.chars = make([]Character, 0)
return &font
}
// Returns a new renderable text object.
func NewText(font *Font, textStr string) *Text {
text := Text{}
text.Actor = NewActor()
text.Pos2D = NewPos2D()
text.TextComponent = &TextComponent{}
text.index = NewVBO(gl.ELEMENT_ARRAY_BUFFER)
text.vertex = NewVBO(gl.ARRAY_BUFFER)
text.texCoord = NewVBO(gl.ARRAY_BUFFER)
text.vao = NewVAO()
text.SetText(font, textStr)
text.Color = Vec4{1, 1, 1, 1}
text.Size = Vec2{1, 1}
text.Scale = Vec2{1, 1}
text.Visible = true
return &text
}
// Creates a new text renderer using given shader, camera and font.
// If shader and/or camera are nil, the default one will be used.
func NewTextRenderer(shader *Shader, camera *Camera, font *Font) *TextRenderer {
if shader == nil {
shader = DefaultTextShader
}
if camera == nil {
camera = DefaultCamera
}
renderer := &TextRenderer{}
renderer.Shader = shader
renderer.Camera = camera
renderer.Font = font
renderer.texts = make([]Text, 0)
renderer.Size = Vec2{1, 1}
renderer.Scale = Vec2{1, 1}
return renderer
return font
}
// Loads characters from JSON file.
@@ -135,7 +82,7 @@ func NewTextRenderer(shader *Shader, camera *Camera, font *Font) *TextRenderer {
// Where x and y start in the upper left corner of the texture, both of type int.
// Offset is optional and can be used to move a character up or down (relative to others).
// If cut is set to true, the characters will be true typed.
func (f *Font) FromJson(path string, cut bool) error {
func (f *Font) LoadFromJson(path string, cut bool) error {
// load file content
content, err := ioutil.ReadFile(path)
@@ -161,22 +108,21 @@ func (f *Font) extractChars(chars []jsonChar, cut bool) {
continue
}
var min, max, size Vec2
var min, max, size geo.Vec2
if !cut {
min = Vec2{char.X * f.tileSize, char.Y * f.tileSize}
max = Vec2{min.X + f.tileSize, min.Y + f.tileSize}
size = Vec2{1, 1}
min = geo.Vec2{char.X * f.tileSize, char.Y * f.tileSize}
max = geo.Vec2{min.X + f.tileSize, min.Y + f.tileSize}
size = geo.Vec2{1, 1}
} else {
min, max, size = f.cutChar(int(char.X), int(char.Y))
}
f.chars = append(f.chars, character{char.Char[0], min, max, size, char.Offset})
f.chars = append(f.chars, Character{char.Char[0], min, max, size, char.Offset})
}
}
func (f *Font) cutChar(x, y int) (Vec2, Vec2, Vec2) {
// find min/max corners of character on texture
func (f *Font) cutChar(x, y int) (geo.Vec2, geo.Vec2, geo.Vec2) {
minX := int(f.Tex.GetSize().X)
minY := int(f.Tex.GetSize().Y)
maxX := 0
@@ -205,21 +151,22 @@ func (f *Font) cutChar(x, y int) (Vec2, Vec2, Vec2) {
}
}
// add padding
minX -= char_padding
maxX += char_padding
minY -= char_padding
maxY += char_padding
texSize := f.Tex.GetSize()
min := Vec2{float64(minX) / texSize.X, float64(maxY) / texSize.Y}
max := Vec2{float64(maxX) / texSize.X, float64(minY) / texSize.Y}
size := Vec2{float64(maxX-minX) / f.tileSize, float64(maxY-minY) / f.tileSize}
min := geo.Vec2{float64(minX) / texSize.X, float64(maxY) / texSize.Y}
max := geo.Vec2{float64(maxX) / texSize.X, float64(minY) / texSize.Y}
// size
size := geo.Vec2{float64(maxX-minX) / f.tileSize, float64(maxY-minY) / f.tileSize}
return min, max, size
}
func (f *Font) getChar(char byte) *character {
func (f *Font) getChar(char byte) *Character {
for _, character := range f.chars {
if character.char == char {
return &character
@@ -229,8 +176,20 @@ func (f *Font) getChar(char byte) *character {
return nil
}
// Deletes GL buffers bound to this text component.
func (t *TextComponent) Drop() {
type Text struct {
*Actor
*Pos2D
id int
text string
bounds geo.Vec2
index, vertex, texCoord *dp.VBO
vao *dp.VAO
}
// Deletes GL buffers bound to this text.
func (t *Text) Drop() {
t.index.Drop()
t.vertex.Drop()
t.texCoord.Drop()
@@ -240,6 +199,7 @@ func (t *TextComponent) Drop() {
// Sets the given string as text and (re)creates buffers.
func (t *Text) SetText(font *Font, text string) {
t.text = text
indices := make([]uint32, len(text)*6)
vertices := make([]float32, len(text)*8)
texCoords := make([]float32, len(text)*8)
@@ -261,7 +221,7 @@ func (t *Text) SetText(font *Font, text string) {
// create vertices/texCoords
index = 0
offset := Vec2{}
offset := geo.Vec2{}
var width, height float64
for i := 0; i < len(text)*8 && int(index) < len(text); i += 8 {
@@ -325,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
t.index.Fill(gl.Ptr(indices[:chars*6]), 4, chars*6, gl.STATIC_DRAW)
t.vertex.Fill(gl.Ptr(vertices[:chars*8]), 4, chars*8, gl.STATIC_DRAW)
t.texCoord.Fill(gl.Ptr(texCoords[:chars*8]), 4, chars*8, gl.STATIC_DRAW)
CheckGLError()
util.CheckGLError()
}
func (t *Text) GetId() int {
return t.id
}
// Returns the text as string.
@@ -341,86 +305,87 @@ func (t *Text) GetText() string {
}
// Returns bounds of text, which is the size of characters.
func (t *Text) GetBounds() Vec2 {
return Vec2{t.bounds.X * t.Size.X * t.Scale.X, t.bounds.Y * t.Size.Y * t.Scale.Y}
func (t *Text) GetBounds() geo.Vec2 {
return geo.Vec2{t.bounds.X * t.Size.X * t.Scale.X, t.bounds.Y * t.Size.Y * t.Scale.Y}
}
// Prepares given text for rendering.
type TextRenderer struct {
Pos2D
Shader *dp.Shader
Camera *Camera
Font *Font
Color geo.Vec4
texts []*Text
}
// Creates a new text renderer using given shader, camera and font.
// If shader and/or camera are nil, the default one will be used.
func NewTextRenderer(shader *dp.Shader, camera *Camera, font *Font) *TextRenderer {
renderer := &TextRenderer{}
renderer.Shader = shader
renderer.Camera = camera
renderer.Font = font
renderer.Color = geo.Vec4{1, 1, 1, 1}
renderer.texts = make([]*Text, 0)
renderer.Size = geo.Vec2{1, 1}
renderer.Scale = geo.Vec2{1, 1}
return renderer
}
// Prepares a text for rendering.
func (r *TextRenderer) Prepare(text *Text) {
text.vao = NewVAO()
text.vao = dp.NewVAO()
text.vao.Bind()
r.Shader.EnableVertexAttribArrays()
text.index.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.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()
}
// 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.
func (r *TextRenderer) Add(actor *Actor, pos *Pos2D, text *TextComponent) bool {
id := actor.GetId()
func (r *TextRenderer) Add(text *Text) {
r.texts = append(r.texts, text)
}
// Returns text by ID.
func (r *TextRenderer) Get(id int) *Text {
for _, text := range r.texts {
if id == text.Actor.GetId() {
return false
if text.GetId() == id {
return text
}
}
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())
return nil
}
// 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 {
if text.Actor.GetId() == id {
if text.GetId() == id {
r.texts = append(r.texts[:i], r.texts[i+1:]...)
return true
return text
}
}
return false
return nil
}
// Removes all texts.
func (r *TextRenderer) RemoveAll() {
r.texts = make([]Text, 0)
}
// Returns number of texts.
func (r *TextRenderer) Len() int {
return len(r.texts)
}
func (r *TextRenderer) GetName() string {
return text_renderer_name
}
// Renders texts.
func (r *TextRenderer) Update(delta float64) {
if r.Font == nil {
return
// Removes all sprites.
func (r *TextRenderer) Clear() {
r.texts = make([]*Text, 0)
}
// Renders sprites.
func (r *TextRenderer) Render() {
r.Shader.Bind()
r.Shader.SendMat3(Default_shader_text_ortho, *MultMat3(r.Camera.CalcOrtho(), r.CalcModel()))
r.Shader.SendUniform1i(Default_shader_text_tex, 0)
r.Shader.SendMat3(TEXTRENDERER_ORTHO, *geo.MultMat3(r.Camera.CalcOrtho(), r.CalcModel()))
r.Shader.SendUniform1i(TEXTRENDERER_TEX, 0)
r.Shader.SendUniform4f(TEXTRENDERER_COLOR, float32(r.Color.X), float32(r.Color.Y), float32(r.Color.Z), float32(r.Color.W))
r.Font.Tex.Bind()
for i := range r.texts {
@@ -429,9 +394,9 @@ func (r *TextRenderer) Update(delta float64) {
}
r.texts[i].vao.Bind()
r.Shader.SendUniform4f(Default_shader_text_color, float32(r.texts[i].Color.X), float32(r.texts[i].Color.Y), float32(r.texts[i].Color.Z), float32(r.texts[i].Color.W))
r.Shader.SendMat3(Default_shader_text_model, *r.texts[i].CalcModel())
r.Shader.SendMat3(TEXTRENDERER_MODEL, *r.texts[i].CalcModel())
gl.DrawElements(gl.TRIANGLES, r.texts[i].index.Size(), gl.UNSIGNED_INT, nil)
}
}
*/

2
vao.go
View File

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

2
vbo.go
View File

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