Rendering now works! Added shaders for reference.

This commit is contained in:
Marvin Blum
2016-05-06 16:53:27 +02:00
parent b0d1f24e3d
commit 3a5b66a20c
13 changed files with 508 additions and 40 deletions

View File

@@ -3,3 +3,4 @@
* ~~cleanup resources~~ * ~~cleanup resources~~
* more logging * more logging
* limit FPS * limit FPS
* fullscreen

129
game.go
View File

@@ -14,6 +14,31 @@ 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"
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);
}`
) )
// If set in RunOptions, the function will be called on window resize. // If set in RunOptions, the function will be called on window resize.
@@ -46,6 +71,10 @@ var (
clearBuffer []uint32 clearBuffer []uint32
viewportWidth int viewportWidth int
viewportHeight int viewportHeight int
// Default resources
DefaultCamera *Camera
Default2DShader *Shader
) )
func init() { func init() {
@@ -130,7 +159,7 @@ func Run(game Game, options *RunOptions) {
// init go-game // init go-game
log.Print("Initializing goga") log.Print("Initializing goga")
initGoga() initGoga(int(width), int(height))
if options != nil && options.Width > 0 && options.Height > 0 { if options != nil && options.Width > 0 && options.Height > 0 {
SetViewport(0, 0, int32(options.Width), int32(options.Height)) SetViewport(0, 0, int32(options.Width), int32(options.Height))
@@ -176,6 +205,60 @@ func Run(game Game, options *RunOptions) {
} }
} }
func initGoga(width, height int) {
// default camera
DefaultCamera = NewCamera(0, 0, width, height)
DefaultCamera.CalcRatio()
DefaultCamera.CalcOrtho()
// default shader
shader, err := NewShader(default_shader_2d_vertex_src, default_shader_2d_fragment_src)
if err != nil {
panic(err)
}
Default2DShader = shader
Default2DShader.BindAttrib(Default_shader_2D_vertex_attrib)
Default2DShader.BindAttrib(Default_shader_2D_texcoord_attrib)
// settings and registration
ClearColorBuffer(true)
EnableAlphaBlending(true)
AddLoader(&PngLoader{gl.LINEAR, false})
AddSystem(NewSpriteRenderer(nil, nil, false))
}
func cleanup() {
// cleanup resources
log.Printf("Cleaning up %v resources", len(resources))
for _, res := range resources {
if drop, ok := res.(Dropable); ok {
drop.Drop()
}
}
// cleanup systems
log.Printf("Cleaning up %v systems", len(systems))
for _, system := range systems {
system.Cleanup()
}
// cleanup scenes
log.Printf("Cleaning up %v scenes", len(scenes))
for _, scene := range scenes {
scene.Cleanup()
}
// cleanup default
log.Print("Cleaning up default resources")
Default2DShader.Drop()
}
// Stops the game and closes the window. // Stops the game and closes the window.
func Stop() { func Stop() {
log.Print("Stopping main loop") log.Print("Stopping main loop")
@@ -202,10 +285,24 @@ func ClearDepthBuffer(do bool) {
} }
} }
// Enables/Disables alpha blending by source alpha channel.
// BLEND = SRC_ALPHA | ONE_MINUS_SRC_ALPHA
func EnableAlphaBlending(enable bool) {
if enable {
gl.Enable(gl.BLEND)
gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
} else {
gl.Disable(gl.BLEND)
}
}
// Sets GL viewport. // Sets GL viewport.
func SetViewport(x, y, width, height int32) { func SetViewport(x, y, width, height int32) {
viewportWidth = int(width) viewportWidth = int(width)
viewportHeight = int(height) viewportHeight = int(height)
DefaultCamera.SetViewport(int(x), int(y), viewportWidth, viewportHeight)
DefaultCamera.CalcRatio()
DefaultCamera.CalcOrtho()
gl.Viewport(x, y, width, height) gl.Viewport(x, y, width, height)
} }
@@ -232,33 +329,3 @@ func removeClearBuffer(buffer uint32) {
} }
} }
} }
func initGoga() {
ClearColorBuffer(true)
AddLoader(&PngLoader{gl.LINEAR, false})
}
func cleanup() {
// cleanup resources
log.Print("Cleaning up resources")
for _, res := range resources {
if drop, ok := res.(Dropable); ok {
drop.Drop()
}
}
// cleanup systems
log.Print("Cleaning up systems")
for _, system := range systems {
system.Cleanup()
}
// cleanup scenes
log.Print("Cleaning up scenes")
for _, scene := range scenes {
scene.Cleanup()
}
}

View File

@@ -13,3 +13,72 @@ func CheckGLError() {
log.Print(error) log.Print(error)
} }
} }
// Creates three VBOs for a 2D rectangle.
func CreateRectMesh(flip bool) (*VBO, *VBO, *VBO) {
ib, vb, tb := createIndexVertexTexCoordBuffer()
indexData := []uint32{0, 1, 2, 1, 2, 3}
vertexData := []float32{0, 0, 1, 0, 0, 1, 1, 1}
texData := make([]float32, 8)
if flip {
texData = []float32{0, 0, 1, 0, 0, 1, 1, 1}
} else {
texData = []float32{0, 1, 1, 1, 0, 0, 1, 0}
}
ib.Fill(gl.Ptr(indexData), 4, 6, gl.STATIC_DRAW)
vb.Fill(gl.Ptr(vertexData), 4, 8, gl.STATIC_DRAW)
tb.Fill(gl.Ptr(texData), 4, 8, gl.STATIC_DRAW)
return ib, vb, tb
}
// Creates three VBOs for a 3D cube mesh.
// Texture coordinates won't map properly.
// This function is supposed to be used for 3D testing.
func CreateCubeMesh() (*VBO, *VBO, *VBO) {
ib, vb, tb := createIndexVertexTexCoordBuffer()
indexData := []uint32{3, 1, 0,
0, 2, 3,
7, 5, 1,
1, 3, 7,
6, 4, 5,
5, 7, 6,
2, 0, 4,
4, 6, 2,
7, 3, 2,
2, 6, 7,
1, 5, 4,
4, 0, 1}
vertexData := []float32{0, 0, 0,
1, 0, 0,
0, 1, 0,
1, 1, 0,
0, 0, 1,
1, 0, 1,
0, 1, 1,
1, 1, 1}
texData := []float32{0, 0,
1, 0,
0, 1,
1, 1,
1, 1,
0, 1,
1, 0,
0, 0}
ib.Fill(gl.Ptr(indexData), 4, 36, gl.STATIC_DRAW)
vb.Fill(gl.Ptr(vertexData), 4, 24, gl.STATIC_DRAW)
tb.Fill(gl.Ptr(texData), 4, 12, gl.STATIC_DRAW)
return ib, vb, tb
}
func createIndexVertexTexCoordBuffer() (*VBO, *VBO, *VBO) {
return NewVBO(gl.ELEMENT_ARRAY_BUFFER),
NewVBO(gl.ARRAY_BUFFER),
NewVBO(gl.ARRAY_BUFFER)
}

85
pos.go Normal file
View File

@@ -0,0 +1,85 @@
package goga
// Position component for 2D objects.
type Pos2D struct {
Pos, Size, Scale, RotPoint Vec2
Rot float64
Visible bool
M Mat3
}
// Creates a default initialized Pos2D.
func NewPos2D() *Pos2D {
m := Mat3{}
m.Identity()
return &Pos2D{Size: Vec2{1, 1}, Scale: Vec2{1, 1}, Visible: true, M: m}
}
// Calculates model matrix for 2D positioning.
func (p *Pos2D) CalcModel() *Mat3 {
p.M.Identity()
p.M.Translate(Vec2{p.Pos.X + p.RotPoint.X, p.Pos.Y + p.RotPoint.Y})
p.M.Rotate(p.Rot)
p.M.Translate(Vec2{-p.RotPoint.X, -p.RotPoint.Y})
p.M.Scale(p.Size)
p.M.Scale(p.Scale)
return &p.M
}
// Returns the center of object.
// Assumes y = 0 is bottom left corner, if not you have to subtract height of object.
func (p *Pos2D) GetCenter() Vec2 {
return Vec2{p.Pos.X + (p.Size.X*p.Scale.X)/2, p.Pos.Y + (p.Size.Y*p.Scale.Y)/2}
}
// Returns true when given point is within rectangle of this object.
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{}
m.Identity()
return &Pos3D{Size: Vec3{1, 1, 1}, Scale: Vec3{1, 1, 1}, Visible: true, M: m}
}
// Calculates model matrix for 3D positioning.
func (p *Pos3D) CalcModel() *Mat4 {
p.M.Identity()
p.M.Translate(Vec3{p.Pos.X + p.RotPoint.X, p.Pos.Y + p.RotPoint.Y, p.Pos.Z + p.RotPoint.Z})
p.M.Rotate(p.Rot.X, Vec3{1, 0, 0})
p.M.Rotate(p.Rot.Y, Vec3{0, 1, 0})
p.M.Rotate(p.Rot.Z, Vec3{0, 0, 1})
p.M.Translate(Vec3{-p.RotPoint.X, -p.RotPoint.Y, -p.RotPoint.Z})
p.M.Scale(p.Size)
p.M.Scale(p.Scale)
return &p.M
}
// Returns the center of object.
// Assumes y = 0 is bottom left corner, if not you have to subtract height of object.
func (p *Pos3D) GetCenter() Vec3 {
return Vec3{p.Pos.X + (p.Size.X*p.Scale.X)/2, p.Pos.Y + (p.Size.Y*p.Scale.Y)/2, p.Pos.Z + (p.Size.Z*p.Scale.Z)/2}
}
// Centers given sprite within rectangle.
// Does nothing if sprite is nil.
// TODO
/*func CenterSprite(sprite *Sprite, x, y, width, height int) {
if sprite == nil {
return
}
sprite.Pos.X = (float64(width-x) - sprite.Size.X) / 2
sprite.Pos.Y = (float64(height-y) - sprite.Size.Y) / 2
}*/

View File

@@ -3,6 +3,7 @@ package goga
import ( import (
"errors" "errors"
"github.com/go-gl/gl/v4.5-core/gl" "github.com/go-gl/gl/v4.5-core/gl"
"log"
"strings" "strings"
) )
@@ -26,8 +27,9 @@ func NewShader(vertexShader, fragmentShader string) (*Shader, error) {
shader.program = gl.CreateProgram() shader.program = gl.CreateProgram()
shader.vertex = gl.CreateShader(gl.VERTEX_SHADER) shader.vertex = gl.CreateShader(gl.VERTEX_SHADER)
shader.fragment = gl.CreateShader(gl.FRAGMENT_SHADER) shader.fragment = gl.CreateShader(gl.FRAGMENT_SHADER)
CheckGLError()
if err := compileShader(&shader.vertex, vertexShader+NullTerminator); err != nil { if err := compileShader(shader.vertex, vertexShader+NullTerminator); err != nil {
gl.DeleteShader(shader.vertex) gl.DeleteShader(shader.vertex)
gl.DeleteShader(shader.fragment) gl.DeleteShader(shader.fragment)
shader.Drop() shader.Drop()
@@ -35,7 +37,7 @@ func NewShader(vertexShader, fragmentShader string) (*Shader, error) {
return nil, err return nil, err
} }
if err := compileShader(&shader.fragment, fragmentShader+NullTerminator); err != nil { if err := compileShader(shader.fragment, fragmentShader+NullTerminator); err != nil {
gl.DeleteShader(shader.vertex) gl.DeleteShader(shader.vertex)
gl.DeleteShader(shader.fragment) gl.DeleteShader(shader.fragment)
shader.Drop() shader.Drop()
@@ -54,21 +56,26 @@ func NewShader(vertexShader, fragmentShader string) (*Shader, error) {
return nil, err return nil, err
} }
CheckGLError()
// we don't need to keep them in memory // we don't need to keep them in memory
gl.DetachShader(shader.program, shader.vertex) gl.DetachShader(shader.program, shader.vertex)
gl.DetachShader(shader.program, shader.fragment) gl.DetachShader(shader.program, shader.fragment)
gl.DeleteShader(shader.vertex) gl.DeleteShader(shader.vertex)
gl.DeleteShader(shader.fragment) gl.DeleteShader(shader.fragment)
CheckGLError()
return shader, nil return shader, nil
} }
func compileShader(shader *uint32, source string) error { func compileShader(shader uint32, source string) error {
csrc := gl.Str(source) log.Print("Compiling shader: " + source)
gl.ShaderSource(*shader, 1, &csrc, nil) csrc, free := gl.Strs(source)
gl.CompileShader(*shader) gl.ShaderSource(shader, 1, csrc, nil)
gl.CompileShader(shader)
free()
if err := shaderCheckError(*shader); err != nil { if err := shaderCheckError(shader); err != nil {
return err return err
} }

12
shader/basic2D.fs Normal file
View File

@@ -0,0 +1,12 @@
#version 130
precision highp float;
uniform sampler2D tex;
in vec2 tc;
out vec4 color;
void main(){
color = texture(tex, tc);
}

13
shader/basic2D.vs Normal file
View File

@@ -0,0 +1,13 @@
#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);
}

12
shader/basic3D.fs Normal file
View File

@@ -0,0 +1,12 @@
#version 130
precision highp float;
uniform sampler2D tex;
in vec2 tc;
out vec4 color;
void main(){
color = texture(tex, tc);
}

13
shader/basic3D.vs Normal file
View File

@@ -0,0 +1,13 @@
#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);
}

13
shader/text.fs Normal file
View File

@@ -0,0 +1,13 @@
#version 130
precision highp float;
uniform sampler2D tex;
uniform vec4 color;
in vec2 tc;
out vec4 c;
void main(){
c = texture(tex, tc)*color;
}

13
shader/text.vs Normal file
View File

@@ -0,0 +1,13 @@
#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);
}

163
sprite.go Normal file
View File

@@ -0,0 +1,163 @@
package goga
import (
"github.com/go-gl/gl/v4.5-core/gl"
)
const (
sprite_renderer_name = "spriteRenderer"
)
// Sprite is an actor having a 2D position and a texture.
type Sprite struct {
*Actor
*Pos2D
*Tex
}
// The sprite renderer is a system rendering sprites.
// It has a 2D position componente, 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{}
sprite.Actor = NewActor()
sprite.Pos2D = NewPos2D()
sprite.Tex = tex
sprite.Size = Vec2{tex.GetSize().X, tex.GetSize().Y}
sprite.Scale = Vec2{1, 1}
sprite.Visible = true
CheckGLError()
return sprite
}
// 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 {
if shader == nil {
shader = Default2DShader
}
if camera == nil {
camera = DefaultCamera
}
renderer := &SpriteRenderer{}
renderer.Shader = shader
renderer.Camera = camera
renderer.sprites = make([]Sprite, 0)
renderer.index, renderer.vertex, renderer.texCoord = CreateRectMesh(flip)
renderer.Size = Vec2{1, 1}
renderer.Scale = Vec2{1, 1}
renderer.vao = NewVAO()
renderer.vao.Bind()
renderer.Shader.EnableVertexAttribArrays()
renderer.index.Bind()
renderer.vertex.Bind()
renderer.vertex.AttribPointer(shader.GetAttribLocation(Default_shader_2D_vertex_attrib), 2, gl.FLOAT, false, 0)
renderer.texCoord.Bind()
renderer.texCoord.AttribPointer(shader.GetAttribLocation(Default_shader_2D_texcoord_attrib), 2, gl.FLOAT, false, 0)
renderer.vao.Unbind()
CheckGLError()
return renderer
}
// Frees recources created by sprite renderer.
// This is called automatically when system gets removed.
func (s *SpriteRenderer) Cleanup() {
s.index.Drop()
s.vertex.Drop()
s.texCoord.Drop()
s.vao.Drop()
}
// Adds sprite to the renderer.
func (s *SpriteRenderer) Add(actor interface{}) bool {
sprite, ok := actor.(*Sprite)
if !ok {
return false
}
s.sprites = append(s.sprites, *sprite)
return true
}
// Removes sprite from renderer.
func (s *SpriteRenderer) Remove(actor interface{}) bool {
sprite, ok := actor.(Sprite)
if !ok {
return false
}
for i, sp := range s.sprites {
if sp == sprite {
s.sprites = append(s.sprites[:i], s.sprites[i+1:]...)
return true
}
}
return false
}
// Removes sprite from renderer by ID.
func (s *SpriteRenderer) RemoveById(id ActorId) bool {
for i, sprite := range s.sprites {
if sprite.Actor.GetId() == id {
s.sprites = append(s.sprites[:i], s.sprites[i+1:]...)
return true
}
}
return false
}
// Removes all sprites from renderer.
func (s *SpriteRenderer) RemoveAll() {
s.sprites = make([]Sprite, 0)
}
// Returns number of sprites.
func (s *SpriteRenderer) Len() int {
return len(s.sprites)
}
func (s *SpriteRenderer) GetName() string {
return sprite_renderer_name
}
// Renders sprites.
func (s *SpriteRenderer) Update(delta float64) {
s.Shader.Bind()
s.Shader.SendMat3(Default_shader_2D_ortho, *MultMat3(s.Camera.CalcOrtho(), s.CalcModel()))
s.Shader.SendUniform1i(Default_shader_2D_tex, 0)
s.vao.Bind()
for i := range s.sprites {
if !s.sprites[i].Visible {
continue
}
s.Shader.SendMat3(Default_shader_2D_model, *s.sprites[i].CalcModel())
s.sprites[i].Tex.Bind()
gl.DrawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, nil)
}
}

View File

@@ -10,8 +10,8 @@ import ()
type System interface { type System interface {
Update(float64) Update(float64)
Cleanup() Cleanup()
Add(*Actor) bool Add(interface{}) bool
Remove(*Actor) bool Remove(interface{}) bool
RemoveById(ActorId) bool RemoveById(ActorId) bool
RemoveAll() RemoveAll()
Len() int Len() int