carpy-breakout/pkg/game/gamewindow.go

356 lines
7.6 KiB
Go

package game
import (
"fmt"
"log"
"git.wisellama.rocks/Wisellama/carpy-breakout/pkg/breakout"
"git.wisellama.rocks/Wisellama/carpy-breakout/pkg/config"
"git.wisellama.rocks/Wisellama/carpy-breakout/pkg/globject"
"git.wisellama.rocks/Wisellama/carpy-breakout/pkg/glshader"
gl "github.com/go-gl/gl/v3.1/gles2"
"github.com/go-gl/mathgl/mgl32"
"github.com/veandco/go-sdl2/sdl"
)
type GLObject interface {
Update()
GLDraw()
GLInit(glProgram uint32)
ToggleWireframe()
}
type GameWindow struct {
SDLWindow *sdl.Window
GLContext *sdl.GLContext
GLProgram uint32
Globject []GLObject
Camera *globject.Camera
Paddle *breakout.Paddle
Ball *breakout.Ball
Targets *breakout.Targets
SideWalls *breakout.SideWalls
AABB *globject.AABB // This AABB represents the inside playable area
keystates map[sdl.Keycode]bool
mouseMoved bool
running bool
fullscreen bool
freelook bool
}
func NewGameWindow(title string) (*GameWindow, error) {
gameWindow := GameWindow{
running: true,
fullscreen: false,
freelook: false,
keystates: make(map[sdl.Keycode]bool),
}
window, err := sdl.CreateWindow(
title,
sdl.WINDOWPOS_UNDEFINED,
sdl.WINDOWPOS_UNDEFINED,
config.SDL_WINDOW_WIDTH,
config.SDL_WINDOW_HEIGHT,
config.SDL_WINDOW_FLAGS)
if err != nil {
log.Println("Failed creating SDL window")
return nil, err
}
gameWindow.SDLWindow = window
context, err := window.GLCreateContext()
if err != nil {
log.Println("Failed Creating GL context.")
return nil, err
}
gameWindow.GLContext = &context
return &gameWindow, err
}
func (w *GameWindow) GLInit(glProgram uint32) {
gl.UseProgram(glProgram)
w.GLProgram = glProgram
w.WindowProjection()
}
func (w *GameWindow) GLInitDefault() error {
glProgram, err := glshader.NewDefaultProgram()
if err != nil {
return err
}
w.GLInit(glProgram)
return nil
}
func (w *GameWindow) AddObject(object GLObject) {
w.Globject = append(w.Globject, object)
}
func (w *GameWindow) Destroy() {
err := w.SDLWindow.Destroy()
if err != nil {
log.Println(err)
}
sdl.GLDeleteContext(*w.GLContext)
}
func (w *GameWindow) ToggleFullscreen() {
var err error
if w.fullscreen {
err = w.SDLWindow.SetFullscreen(config.SDL_WINDOW_FLAGS)
if err != nil {
log.Printf("error setting default window flags: %v", err)
}
} else {
err = w.SDLWindow.SetFullscreen(config.SDL_FULLSCREEN_WINDOW_FLAGS)
if err != nil {
log.Printf("error setting fullscreen window flags: %v", err)
}
}
w.fullscreen = !w.fullscreen
}
func (w *GameWindow) ToggleFreelook() {
w.freelook = !w.freelook
}
func (w *GameWindow) IsFreelookOn() bool {
return w.freelook
}
func (w *GameWindow) IsRunning() bool {
return w.running
}
func (w *GameWindow) StopRunning() {
w.running = false
}
func (w *GameWindow) WindowProjection() {
width, height := w.SDLWindow.GLGetDrawableSize()
projection := mgl32.Perspective(mgl32.DegToRad(45.0), float32(width)/float32(height), 0.01, 1000.0)
glshader.SetUniformMatrix4f(w.GLProgram, "projection", &projection)
gl.Viewport(0, 0, width, height)
}
func (w *GameWindow) SetPaddle(paddle *breakout.Paddle) {
w.Paddle = paddle
}
func (w *GameWindow) SetBall(ball *breakout.Ball) {
w.Ball = ball
}
func (w *GameWindow) SetTargets(targets *breakout.Targets) {
w.Targets = targets
}
func (w *GameWindow) SetSideWalls(sidewalls *breakout.SideWalls) {
w.SideWalls = sidewalls
}
func (w *GameWindow) SetCamera(camera *globject.Camera) {
w.Camera = camera
}
func (w *GameWindow) ToggleWireframe() {
// TODO figure out concurrency
// var wg sync.WaitGroup
// for _, o := range w.Globject {
// wg.Add(1)
// go func() {
// o.ToggleWireframe()
// wg.Done()
// }()
// }
// wg.Wait()
for _, o := range w.Globject {
o.ToggleWireframe()
}
}
func (w *GameWindow) GLDraw() {
model := mgl32.Ident4()
// Probably can't do concurrent drawing due to OpenGL
for _, o := range w.Globject {
glshader.SetUniformMatrix4f(w.GLProgram, "model", &model)
o.GLDraw()
}
}
func (w *GameWindow) Update() {
// TODO figure out concurrency
// var wg sync.WaitGroup
// for _, o := range w.Globject {
// wg.Add(1)
// go func() {
// o.Update()
// wg.Done()
// }()
// }
// wg.Wait()
if w.IsFreelookOn() {
w.handleFreelookMovement()
} else if !w.mouseMoved {
w.handleGameKeyboardMovement()
}
w.mouseMoved = false
w.Camera.Update()
w.Ball.Update()
w.Ball.HandlePaddleCollision(w.Paddle)
w.Ball.HandleTargetCollisions(w.Targets)
w.Ball.HandleSideWallsCollisions(w.SideWalls)
w.Paddle.Update()
w.ensurePaddleBoundary()
}
func (w *GameWindow) HandleEvents() {
for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
switch e := event.(type) {
case *sdl.WindowEvent:
w.WindowProjection()
case *sdl.QuitEvent:
log.Println("Quiting")
w.StopRunning()
case *sdl.MouseMotionEvent:
w.handleMouseMovementEvent(e)
case *sdl.MouseButtonEvent:
fmt.Printf("[%d ms] MouseButton\ttype:%d\tid:%d\tx:%d\ty:%d\tbutton:%d\tstate:%d\n",
e.Timestamp, e.Type, e.Which, e.X, e.Y, e.Button, e.State)
case *sdl.MouseWheelEvent:
fmt.Printf("[%d ms] MouseWheel\ttype:%d\tid:%d\tx:%d\ty:%d\n",
e.Timestamp, e.Type, e.Which, e.X, e.Y)
case *sdl.KeyboardEvent:
w.HandleKeyboardEvent(e)
}
}
}
func (w *GameWindow) HandleKeyboardEvent(e *sdl.KeyboardEvent) {
// Key toggles (directly use the keyboard events to something)
if e.Keysym.Sym == sdl.K_ESCAPE {
log.Println("Esc quitting")
w.StopRunning()
}
if e.Keysym.Sym == sdl.K_f {
if e.Type == sdl.KEYDOWN {
w.ToggleFullscreen()
}
}
if e.Keysym.Sym == sdl.K_l {
if e.Type == sdl.KEYDOWN {
w.ToggleWireframe()
}
}
if e.Keysym.Sym == sdl.K_k {
if e.Type == sdl.KEYDOWN {
w.ToggleFreelook()
}
}
// Key states (just set a boolean whether the key is actively being pressed)
if e.Type == sdl.KEYDOWN {
w.keystates[e.Keysym.Sym] = true
} else if e.Type == sdl.KEYUP {
w.keystates[e.Keysym.Sym] = false
}
}
func (w *GameWindow) handleFreelookMovement() {
// Forward/Back
if w.keystates[sdl.K_w] {
w.Camera.SetZVelocity(1)
} else if w.keystates[sdl.K_s] {
w.Camera.SetZVelocity(-1)
} else {
w.Camera.SetZVelocity(0)
}
// Left/Right
if w.keystates[sdl.K_d] {
w.Camera.SetXVelocity(1)
} else if w.keystates[sdl.K_a] {
w.Camera.SetXVelocity(-1)
} else {
w.Camera.SetXVelocity(0)
}
// Up/Down
if w.keystates[sdl.K_e] {
w.Camera.SetYVelocity(1)
} else if w.keystates[sdl.K_q] {
w.Camera.SetYVelocity(-1)
} else {
w.Camera.SetYVelocity(0)
}
// Spin
if w.keystates[sdl.K_x] {
w.Camera.SetRotationVelocity(1)
} else if w.keystates[sdl.K_z] {
w.Camera.SetRotationVelocity(-1)
} else {
w.Camera.SetRotationVelocity(0)
}
// Faster
if w.keystates[sdl.K_LSHIFT] {
w.Camera.SetFastMoveVelocity(2)
} else {
w.Camera.SetFastMoveVelocity(0)
}
}
func (w *GameWindow) handleMouseMovementEvent(e *sdl.MouseMotionEvent) {
if e.XRel != 0 || e.YRel != 0 {
w.mouseMoved = true
if w.IsFreelookOn() {
w.Camera.Rotate(e.XRel, e.YRel, 0)
} else {
// Paddle movement
v := float32(e.XRel) / 50.0
w.Paddle.Box.SetXVelocity(v)
}
}
}
func (w *GameWindow) handleGameKeyboardMovement() {
if w.keystates[sdl.K_d] {
w.Paddle.Box.SetXVelocity(0.5)
} else if w.keystates[sdl.K_a] {
w.Paddle.Box.SetXVelocity(-0.5)
} else {
w.Paddle.Box.SetXVelocity(0)
}
}
func (w *GameWindow) ensurePaddleBoundary() {
b := w.Paddle.Box
offset := b.Width / 2.0
inside := w.AABB
furthestLeft := inside.BottomLeft.X() + offset
furthestRight := inside.TopRight.X() - offset
if b.Translation.X() < furthestLeft {
w.Paddle.Box.Translation[0] = furthestLeft
} else if b.Translation.X() > furthestRight {
w.Paddle.Box.Translation[0] = furthestRight
}
}