forked from g3n/engine
-
Notifications
You must be signed in to change notification settings - Fork 0
/
perspective.go
151 lines (120 loc) · 4.21 KB
/
perspective.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
// Copyright 2016 The G3N Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package camera
import (
"github.com/g3n/engine/core"
"github.com/g3n/engine/math32"
)
// Perspective is a perspective camera.
type Perspective struct {
Camera // Embedded camera
fov float32 // field of view in degrees
aspect float32 // aspect ratio (width/height)
near float32 // near plane z coordinate
far float32 // far plane z coordinate
projChanged bool // camera projection parameters changed (needs to recalculates projection matrix)
projMatrix math32.Matrix4 // last calculated projection matrix
}
// NewPerspective creates and returns a pointer to a new perspective camera with the
// specified parameters.
func NewPerspective(fov, aspect, near, far float32) *Perspective {
cam := new(Perspective)
cam.Camera.Initialize()
cam.fov = fov
cam.aspect = aspect
cam.near = near
cam.far = far
cam.projChanged = true
return cam
}
// SetFov sets the camera field of view in degrees.
func (cam *Perspective) SetFov(fov float32) {
cam.fov = fov
cam.projChanged = true
}
// SetAspect sets the camera aspect ratio (width/height).
func (cam *Perspective) SetAspect(aspect float32) {
cam.aspect = aspect
cam.projChanged = true
}
// Fov returns the current camera FOV (field of view) in degrees.
func (cam *Perspective) Fov() float32 {
return cam.fov
}
// Aspect returns the current camera aspect ratio.
func (cam Perspective) Aspect() float32 {
return cam.aspect
}
// Near returns the current camera near plane Z coordinate.
func (cam *Perspective) Near() float32 {
return cam.near
}
// Far returns the current camera far plane Z coordinate.
func (cam *Perspective) Far() float32 {
return cam.far
}
// ProjMatrix satisfies the ICamera interface.
func (cam *Perspective) ProjMatrix(m *math32.Matrix4) {
cam.updateProjMatrix()
*m = cam.projMatrix
}
// Project transforms the specified position from world coordinates to this camera projected coordinates.
func (cam *Perspective) Project(v *math32.Vector3) (*math32.Vector3, error) {
// Get camera view matrix
var matrix math32.Matrix4
matrixWorld := cam.MatrixWorld()
err := matrix.GetInverse(&matrixWorld)
if err != nil {
return nil, err
}
// Update camera projection matrix
cam.updateProjMatrix()
// Multiply viewMatrix by projMatrix and apply the resulting projection matrix to the provided vector
matrix.MultiplyMatrices(&cam.projMatrix, &matrix)
v.ApplyProjection(&matrix)
return v, nil
}
// Unproject transforms the specified position from camera projected coordinates to world coordinates.
func (cam *Perspective) Unproject(v *math32.Vector3) (*math32.Vector3, error) {
// Get inverted camera view matrix
invertedViewMatrix := cam.MatrixWorld()
// Get inverted camera projection matrix
cam.updateProjMatrix()
var invertedProjMatrix math32.Matrix4
err := invertedProjMatrix.GetInverse(&cam.projMatrix)
if err != nil {
return nil, err
}
// Multiply invertedViewMatrix by invertedProjMatrix
// to get transformation from camera projected coordinates to world coordinates
// and project vector using this transformation
var matrix math32.Matrix4
matrix.MultiplyMatrices(&invertedViewMatrix, &invertedProjMatrix)
v.ApplyProjection(&matrix)
return v, nil
}
// SetRaycaster sets the specified raycaster with this camera position in world coordinates
// pointing to the direction defined by the specified coordinates unprojected using this camera.
func (cam *Perspective) SetRaycaster(rc *core.Raycaster, sx, sy float32) error {
var origin, direction math32.Vector3
matrixWorld := cam.MatrixWorld()
origin.SetFromMatrixPosition(&matrixWorld)
direction.Set(sx, sy, 0.5)
unproj, err := cam.Unproject(&direction)
if err != nil {
return err
}
unproj.Sub(&origin).Normalize()
rc.Set(&origin, &direction)
// Updates the view matrix of the raycaster
cam.ViewMatrix(&rc.ViewMatrix)
return nil
}
// updateProjMatrix updates the projection matrix if necessary.
func (cam *Perspective) updateProjMatrix() {
if cam.projChanged {
cam.projMatrix.MakePerspective(cam.fov, cam.aspect, cam.near, cam.far)
cam.projChanged = false
}
}