-
Notifications
You must be signed in to change notification settings - Fork 7
/
LimitedCamera.go
98 lines (78 loc) · 2.8 KB
/
LimitedCamera.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
package levels
import (
"math"
mgl "github.com/go-gl/mathgl/mgl32"
)
// LimitedCamera is a camera implementation with ranged controls for zooming and moving.
type LimitedCamera struct {
viewportWidth, viewportHeight float32
minZoom, maxZoom float32
minPos, maxPos float32
requestedZoomLevel float32
viewOffsetX, viewOffsetY float32
viewMatrix mgl.Mat4
}
// NewLimitedCamera returns a new instance of a LimitedCamera.
func NewLimitedCamera(minZoom, maxZoom float32, minPos, maxPos float32) *LimitedCamera {
cam := &LimitedCamera{
viewportWidth: 1.0,
viewportHeight: 1.0,
minZoom: minZoom,
maxZoom: maxZoom,
minPos: minPos,
maxPos: maxPos,
viewMatrix: mgl.Ident4()}
return cam
}
// SetViewportSize notifies the camera how big the view is.
func (cam *LimitedCamera) SetViewportSize(width, height float32) {
if (cam.viewportWidth != width) || (cam.viewportHeight != height) {
cam.viewportWidth, cam.viewportHeight = width, height
cam.updateViewMatrix()
}
}
// ViewMatrix implements the Viewer interface.
func (cam *LimitedCamera) ViewMatrix() *mgl.Mat4 {
return &cam.viewMatrix
}
// MoveBy adjusts the requested view offset by given delta values in world coordinates.
func (cam *LimitedCamera) MoveBy(dx, dy float32) {
cam.MoveTo(cam.viewOffsetX+dx, cam.viewOffsetY+dy)
}
// MoveTo sets the requested view offset to the given world coordinates.
func (cam *LimitedCamera) MoveTo(worldX, worldY float32) {
cam.viewOffsetX = cam.limitValue(worldX, -cam.maxPos, cam.minPos)
cam.viewOffsetY = cam.limitValue(worldY, -cam.maxPos, cam.minPos)
cam.updateViewMatrix()
}
// ZoomAt adjusts the requested zoom level by given delta, centered around given world position.
// Positive values zoom in.
func (cam *LimitedCamera) ZoomAt(levelDelta float32, x, y float32) {
cam.requestedZoomLevel = cam.limitValue(cam.requestedZoomLevel+levelDelta, cam.minZoom, cam.maxZoom)
focusPoint := mgl.Vec4{x, y, 0.0, 1.0}
oldPixel := cam.viewMatrix.Mul4x1(focusPoint)
cam.updateViewMatrix()
newPixel := cam.viewMatrix.Mul4x1(focusPoint)
scaleFactor := cam.scaleFactor()
cam.MoveBy(-(newPixel[0]-oldPixel[0])/scaleFactor, +(newPixel[1]-oldPixel[1])/scaleFactor)
}
func (cam *LimitedCamera) limitValue(value float32, min, max float32) float32 {
result := value
if result < min {
result = min
}
if result > max {
result = max
}
return result
}
func (cam *LimitedCamera) scaleFactor() float32 {
return float32(math.Pow(2.0, float64(cam.requestedZoomLevel)))
}
func (cam *LimitedCamera) updateViewMatrix() {
scaleFactor := cam.scaleFactor()
cam.viewMatrix = mgl.Ident4().
Mul4(mgl.Translate3D(cam.viewportWidth/2.0, cam.viewportHeight/2.0, 0)).
Mul4(mgl.Scale3D(scaleFactor, -scaleFactor, 1.0)).
Mul4(mgl.Translate3D(cam.viewOffsetX, cam.viewOffsetY, 0))
}