From 62194c4cadf67a2c32f2dd4ce08d023f208a792d Mon Sep 17 00:00:00 2001 From: bloeys Date: Sat, 1 Oct 2022 01:43:01 +0400 Subject: [PATCH] FPS camera with pitch and yaw --- camera/camera.go | 14 ++++-- go.mod | 8 ++-- go.sum | 16 +++---- main.go | 115 ++++++++++++++++++++++++++++++++++------------- 4 files changed, 106 insertions(+), 47 deletions(-) diff --git a/camera/camera.go b/camera/camera.go index a992f51..2b5cf58 100755 --- a/camera/camera.go +++ b/camera/camera.go @@ -38,7 +38,7 @@ type Camera struct { // Should be called whenever a camera parameter changes func (c *Camera) Update() { - c.ViewMat = gglm.LookAt(&c.Pos, c.Pos.Clone().Add(&c.Forward), &c.WorldUp).Mat4 + c.ViewMat = gglm.LookAtRH(&c.Pos, c.Pos.Clone().Add(&c.Forward), &c.WorldUp).Mat4 if c.Type == Type_Perspective { c.ProjMat = *gglm.Perspective(c.Fov, c.AspectRatio, c.NearClip, c.FarClip) @@ -47,9 +47,15 @@ func (c *Camera) Update() { } } -func (c *Camera) LookAt(forward, worldUp *gglm.Vec3) { - c.Forward = *forward - c.WorldUp = *worldUp +// UpdateRotation calculates a new forward vector and then calls camera.Update() +func (c *Camera) UpdateRotation(pitch, yaw float32) { + + dir := gglm.NewVec3( + gglm.Cos32(yaw)*gglm.Cos32(pitch), + gglm.Sin32(pitch), + gglm.Sin32(yaw)*gglm.Cos32(pitch), + ) + c.Forward = *dir.Normalize() c.Update() } diff --git a/go.mod b/go.mod index 8ded7c0..c4c64b3 100755 --- a/go.mod +++ b/go.mod @@ -2,12 +2,12 @@ module github.com/bloeys/nmage go 1.18 -require github.com/veandco/go-sdl2 v0.4.10 +require github.com/veandco/go-sdl2 v0.4.25 -require github.com/go-gl/gl v0.0.0-20211025173605-bda47ffaa784 +require github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 require ( github.com/bloeys/assimp-go v0.4.2 - github.com/bloeys/gglm v0.41.10 - github.com/inkyblackness/imgui-go/v4 v4.5.0 + github.com/bloeys/gglm v0.42.0 + github.com/inkyblackness/imgui-go/v4 v4.6.0 ) diff --git a/go.sum b/go.sum index 8f67548..a4074a2 100755 --- a/go.sum +++ b/go.sum @@ -1,17 +1,17 @@ github.com/bloeys/assimp-go v0.4.2 h1:ArVK74BCFcTO/rCGj2NgZG9xtbjnJdEn5npIeJx1Z04= github.com/bloeys/assimp-go v0.4.2/go.mod h1:my3yRxT7CfOztmvi+0svmwbaqw0KFrxaHxncoyaEIP0= -github.com/bloeys/gglm v0.41.10 h1:R9FMiI+VQVXAI+vDwCB7z9xqzy5VAR1657u8TQTDNKA= -github.com/bloeys/gglm v0.41.10/go.mod h1:qwJQ0WzV191wAMwlGicbfbChbKoSedMk7gFFX6GnyOk= +github.com/bloeys/gglm v0.42.0 h1:UAUFGTaZv3dpZ0YSIQVum3bdeCZgNmx965VLnD2v11k= +github.com/bloeys/gglm v0.42.0/go.mod h1:qwJQ0WzV191wAMwlGicbfbChbKoSedMk7gFFX6GnyOk= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-gl/gl v0.0.0-20211025173605-bda47ffaa784 h1:1Zi56D0LNfvkzM+BdoxKryvUEdyWO7LP8oRT+oSYJW0= -github.com/go-gl/gl v0.0.0-20211025173605-bda47ffaa784/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw= -github.com/inkyblackness/imgui-go/v4 v4.5.0 h1:iUon7q0Hr0c0/Gc2V6bTP7Anu9WH7H26dI7JkPBmMEA= -github.com/inkyblackness/imgui-go/v4 v4.5.0/go.mod h1:g8SAGtOYUP7rYaOB2AsVKCEHmPMDmJKgt4z6d+flhb0= +github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 h1:zDw5v7qm4yH7N8C8uWd+8Ii9rROdgWxQuGoJ9WDXxfk= +github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw= +github.com/inkyblackness/imgui-go/v4 v4.6.0 h1:ShcnXEYl80+xREGBY9OpGWePA6FfJChY9Varsm+3jjE= +github.com/inkyblackness/imgui-go/v4 v4.6.0/go.mod h1:g8SAGtOYUP7rYaOB2AsVKCEHmPMDmJKgt4z6d+flhb0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/veandco/go-sdl2 v0.4.10 h1:8QoD2bhWl7SbQDflIAUYWfl9Vq+mT8/boJFAUzAScgY= -github.com/veandco/go-sdl2 v0.4.10/go.mod h1:OROqMhHD43nT4/i9crJukyVecjPNYYuCofep6SNiAjY= +github.com/veandco/go-sdl2 v0.4.25 h1:J5ac3KKOccp/0xGJA1PaNYKPUcZm19IxhDGs8lJofPI= +github.com/veandco/go-sdl2 v0.4.25/go.mod h1:OROqMhHD43nT4/i9crJukyVecjPNYYuCofep6SNiAjY= diff --git a/main.go b/main.go index 88057ab..9f11876 100755 --- a/main.go +++ b/main.go @@ -36,20 +36,23 @@ import ( // Material system editor with fields automatically extracted from the shader const ( - camSpeed float32 = 15 + camSpeed = 15 + mouseSensitivity = 0.5 ) var ( window *engine.Window - cam *camera.Camera + pitch float32 = 0 + yaw float32 = -90 + cam *camera.Camera simpleMat *materials.Material cubeMesh *meshes.Mesh cubeModelMat = gglm.NewTrMatId() - lightPos1 = gglm.NewVec3(2, 2, 0) + lightPos1 = gglm.NewVec3(-2, 0, 2) lightColor1 = gglm.NewVec3(1, 1, 1) ) @@ -175,8 +178,8 @@ func (g *OurGame) Init() { // Camera winWidth, winHeight := g.Win.SDLWin.GetSize() cam = camera.NewPerspective( - gglm.NewVec3(0, 0, -10), - gglm.NewVec3(0, 0, 1), + gglm.NewVec3(0, 0, 10), + gglm.NewVec3(0, 0, -1), gglm.NewVec3(0, 1, 0), 0.1, 20, 45*gglm.Deg2Rad, @@ -192,36 +195,28 @@ func (g *OurGame) Init() { } +func vecRotByQuat(v *gglm.Vec3, q *gglm.Quat) *gglm.Vec3 { + + // Reference: https://gamedev.stackexchange.com/questions/28395/rotating-vector3-by-a-quaternion + qVec := gglm.NewVec3(q.X(), q.Y(), q.Z()) + + rotatedVec := qVec.Clone().Scale(2 * gglm.DotVec3(v, qVec)) + + t1 := q.W()*q.W() - gglm.DotVec3(qVec, qVec) + rotatedVec.Add(v.Clone().Scale(t1)) + + rotatedVec.Add(gglm.Cross(qVec, v).Scale(2 * q.W())) + return rotatedVec +} + func (g *OurGame) Update() { if input.IsQuitClicked() || input.KeyClicked(sdl.K_ESCAPE) { engine.Quit() } - //Camera movement - if input.KeyDown(sdl.K_w) { - cam.Pos.Add(cam.WorldUp.Clone().Scale(camSpeed * timing.DT())) - updateViewMat() - } else if input.KeyDown(sdl.K_s) { - cam.Pos.Sub(cam.WorldUp.Clone().Scale(camSpeed * timing.DT())) - updateViewMat() - } - - if input.KeyDown(sdl.K_d) { - cam.Pos.Add(gglm.Cross(&cam.WorldUp, &cam.Forward).Scale(camSpeed * timing.DT())) - updateViewMat() - } else if input.KeyDown(sdl.K_a) { - cam.Pos.Sub(gglm.Cross(&cam.WorldUp, &cam.Forward).Scale(camSpeed * timing.DT())) - updateViewMat() - } - - if input.GetMouseWheelYNorm() > 0 { - cam.Pos.Add(&cam.Forward) - updateViewMat() - } else if input.GetMouseWheelYNorm() < 0 { - cam.Pos.Sub(&cam.Forward) - updateViewMat() - } + g.updateCameraLookAround() + g.updateCameraPos() //Rotating cubes if input.KeyDown(sdl.K_SPACE) { @@ -229,10 +224,68 @@ func (g *OurGame) Update() { simpleMat.SetUnifMat4("modelMat", &cubeModelMat.Mat4) } - imgui.DragFloat3("Cam Pos", &cam.Pos.Data) + if imgui.DragFloat3("Cam Pos", &cam.Pos.Data) { + updateViewMat() + } + if imgui.DragFloat3("Cam Forward", &cam.Forward.Data) { + updateViewMat() + } if input.KeyClicked(sdl.K_F4) { - fmt.Printf("Pos: %s; Forward: %s; Forward*WorldUp: %s\n", cam.Pos.String(), cam.Forward.String(), gglm.Cross(&cam.Forward, &cam.WorldUp)) + fmt.Printf("Pos: %s; Forward: %s; |Forward|: %f\n", cam.Pos.String(), cam.Forward.String(), cam.Forward.Mag()) + } +} + +func (g *OurGame) updateCameraLookAround() { + + mouseX, mouseY := input.GetMouseMotion() + if mouseX == 0 && mouseY == 0 { + return + } + + // Yaw + yaw += float32(mouseX) * mouseSensitivity * timing.DT() + + // Pitch + pitch += float32(-mouseY) * mouseSensitivity * timing.DT() + if pitch > 89.0 { + pitch = 89.0 + } + + if pitch < -89.0 { + pitch = -89.0 + } + + // Update cam forward + cam.UpdateRotation(pitch, yaw) + + updateViewMat() +} + +func (g *OurGame) updateCameraPos() { + + update := false + + // Forward and backward + if input.KeyDown(sdl.K_w) { + cam.Pos.Add(cam.Forward.Clone().Scale(camSpeed * timing.DT())) + update = true + } else if input.KeyDown(sdl.K_s) { + cam.Pos.Add(cam.Forward.Clone().Scale(-camSpeed * timing.DT())) + update = true + } + + // Left and right + if input.KeyDown(sdl.K_d) { + cam.Pos.Add(gglm.Cross(&cam.Forward, &cam.WorldUp).Normalize().Scale(camSpeed * timing.DT())) + update = true + } else if input.KeyDown(sdl.K_a) { + cam.Pos.Add(gglm.Cross(&cam.Forward, &cam.WorldUp).Normalize().Scale(-camSpeed * timing.DT())) + update = true + } + + if update { + updateViewMat() } }