/
fps.go
186 lines (159 loc) · 5.58 KB
/
fps.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
// Package camera provides implementations of different camera models.
package fps
import (
"math"
"github.com/go-gl/glfw/v3.2/glfw"
"github.com/go-gl/mathgl/mgl32"
)
// FPS moves in the view direction while the viewing direction can be changed.
type FPS struct {
width int
height int
theta float32
phi float32
dir mgl32.Vec3
speed float32
Pos mgl32.Vec3
Target mgl32.Vec3
Up mgl32.Vec3
Right mgl32.Vec3
Fov float32
Near float32
Far float32
}
// MakeDefault creates a FPS camera with the viewport of width and height and a position.
// It assumes a field of view of 45 degrees and a near and far plane at 0.1 and 100.0 respectively.
func MakeDefault(width, height int, pos mgl32.Vec3, speed float32) FPS {
return Make(width, height, pos, speed, 45, 0.1, 1000.0)
}
// NewDefault creates a reference to a FPS camera with the viewport of width and height and a position.
// It assumes a field of view of 45 degrees and a near and far plane at 0.1 and 100.0 respectively.
func NewDefault(width, height int, pos mgl32.Vec3, speed float32) *FPS {
return New(width, height, pos, speed, 45, 0.1, 1000.0)
}
// Make creates a FPS with the viewport of width and height and a radius from the origin.
// It assumes a field of view of 45 degrees and a near and far plane at 0.1 and 100.0 respectively.
func Make(width, height int, pos mgl32.Vec3, speed, fov, near, far float32) FPS {
dir := mgl32.Vec3{0.0, 0.0, 1.0}
camera := FPS{
width: width,
height: height,
theta: 90.0,
phi: 90.0,
dir: dir,
speed: speed,
Pos: pos,
Target: pos.Add(dir),
Up: mgl32.Vec3{0, 1, 0},
Right: mgl32.Vec3{1, 0, 0},
Fov: fov,
Near: near,
Far: far,
}
camera.Update()
return camera
}
// New creates a reference to a FPS with the viewport of width and height and a radius from the origin.
// It assumes a field of view of 45 degrees and a near and far plane at 0.1 and 100.0 respectively.
func New(width, height int, pos mgl32.Vec3, speed, fov, near, far float32) *FPS {
camera := Make(width, height, pos, speed, fov, near, far)
return &camera
}
// Update recalculates the position of the camera.
// Call it every time after calling Rotate or Zoom.
func (camera *FPS) Update() {
theta := mgl32.DegToRad(camera.theta)
phi := mgl32.DegToRad(camera.phi)
// sphere coordinates with inverse y
btheta := float64(theta)
bphi := float64(phi)
camera.dir = mgl32.Vec3{
float32(math.Sin(btheta) * math.Cos(bphi)),
-float32(math.Cos(btheta)),
float32(math.Sin(btheta) * math.Sin(bphi)),
}
camera.dir = camera.dir.Normalize()
// set target
camera.Target = camera.Pos.Add(camera.dir)
// calculate up vector
look := camera.dir.Mul(-1)
worldUp := mgl32.Vec3{0.0, 1.0, 0.0}
camera.Right = worldUp.Cross(look).Normalize()
camera.Up = look.Cross(camera.Right)
}
// Rotate adds delta angles in degrees to the theta and phi angles.
// Where theta is the vertical angle and phi the horizontal angle.
func (camera *FPS) Rotate(theta, phi float32) {
camera.theta += theta
camera.phi += phi
// limit angles
camera.theta = float32(math.Max(math.Min(float64(camera.theta), 179.9), 0.01))
if camera.phi < 0 {
camera.phi = 360 + camera.phi
} else if camera.phi >= 360 {
camera.phi = camera.phi - 360
}
}
// Zoom changes the radius of the camera to the target point.
func (camera *FPS) Zoom(distance float32) {}
// GetPos returns the position of the camera in worldspace
func (camera *FPS) GetPos() mgl32.Vec3 {
return camera.Pos
}
// GetView returns the view matrix of the camera.
func (camera *FPS) GetView() mgl32.Mat4 {
return mgl32.LookAtV(camera.Pos, camera.Target, camera.Up)
}
// GetPerspective returns the perspective projection of the camera.
func (camera *FPS) GetPerspective() mgl32.Mat4 {
fov := mgl32.DegToRad(camera.Fov)
aspect := float32(camera.width) / float32(camera.height)
return mgl32.Perspective(fov, aspect, camera.Near, camera.Far)
}
// GetOrtho returns the orthographic projection of the camera.
func (camera *FPS) GetOrtho() mgl32.Mat4 {
angle := camera.Fov * math.Pi / 180.0
dfar := float32(math.Tan(float64(angle/2.0))) * camera.Far
d := dfar
return mgl32.Ortho(-d, d, -d, d, camera.Near, camera.Far)
}
// GetViewPerspective returns P*V.
func (camera *FPS) GetViewPerspective() mgl32.Mat4 {
return camera.GetPerspective().Mul4(camera.GetView())
}
// SetPos updates the target point of the camera.
// It requires to call Update to take effect.
func (camera *FPS) SetPos(pos mgl32.Vec3) {
camera.Pos = pos
camera.Target = camera.Pos.Add(camera.dir)
}
// OnCursorPosMove is a callback handler that is called every time the cursor moves.
func (camera *FPS) OnCursorPosMove(x, y, dx, dy float64) bool {
dPhi := float32(-dx) / 2.0
dTheta := float32(-dy) / 2.0
camera.Rotate(dTheta, -dPhi)
return false
}
// OnMouseButtonPress is a callback handler that is called every time a mouse button is pressed or released.
func (camera *FPS) OnMouseButtonPress(leftPressed, rightPressed bool) bool {
return false
}
// OnMouseScroll is a callback handler that is called every time the mouse wheel moves.
func (camera *FPS) OnMouseScroll(x, y float64) bool {
return false
}
// OnKeyPress is a callback handler that is called every time a keyboard key is pressed.
func (camera *FPS) OnKeyPress(key, action, mods int) bool {
dir := camera.dir.Mul(camera.speed)
right := camera.Right.Mul(camera.speed)
if key == int(glfw.KeyW) {
camera.Pos = camera.Pos.Add(dir)
} else if key == int(glfw.KeyS) {
camera.Pos = camera.Pos.Sub(dir)
} else if key == int(glfw.KeyA) {
camera.Pos = camera.Pos.Sub(right)
} else if key == int(glfw.KeyD) {
camera.Pos = camera.Pos.Add(right)
}
return false
}