1 /**
2  * Defines the Camera class, which manages a view and projection matrix.
3  */
4 module dash.components.camera;
5 import dash.core, dash.components, dash.graphics, dash.utility;
6 
7 import std.conv: to;
8 import std.math: sin, cos;
9 
10 mixin( registerComponents!() );
11 
12 /**
13  * Camera manages a view and projection matrix.
14  */
15 final class Camera : Component, IDirtyable
16 {
17 private:
18     float _prevFov, _prevNear, _prevFar, _prevWidth, _prevHeight;
19 
20     vec2f _projectionConstants; // For rebuilding linear Z in shaders
21     mat4f _prevLocalMatrix;
22     mat4f _viewMatrix;
23     mat4f _inverseViewMatrix;
24     mat4f _perspectiveMatrix;
25     mat4f _inversePerspectiveMatrix;
26     mat4f _orthogonalMatrix;
27     mat4f _inverseOrthogonalMatrix;
28 
29 public:
30     /// TODO
31     mixin( ThisDirtyGetter!( _viewMatrix, updateViewMatrix ) );
32     /// TODO
33     mixin( ThisDirtyGetter!( _inverseViewMatrix, updateViewMatrix ) );
34     @rename( "FOV" )
35     float fov;
36     @rename( "Near" )
37     float near;
38     @rename( "Far" )
39     float far;
40 
41     /**
42      * TODO
43      *
44      * Returns:
45      */
46     final vec2f projectionConstants()
47     {
48         if( this.projectionDirty )
49         {
50             updatePerspective();
51             updateOrthogonal();
52             updateProjectionDirty();
53         }
54 
55         return _projectionConstants;
56     }
57 
58     /**
59      * TODO
60      *
61      * Returns:
62      */
63     final mat4f perspectiveMatrix()
64     {
65         if( this.projectionDirty )
66         {
67             updatePerspective();
68             updateOrthogonal();
69             updateProjectionDirty();
70         }
71 
72         return _perspectiveMatrix;
73     }
74 
75     /**
76      * TODO
77      *
78      * Returns:
79      */
80     final mat4f inversePerspectiveMatrix()
81     {
82         if( this.projectionDirty )
83         {
84             updatePerspective();
85             updateOrthogonal();
86             updateProjectionDirty();
87         }
88 
89         return _inversePerspectiveMatrix;
90     }
91 
92     /**
93      * TODO
94      *
95      * Returns:
96      */
97     final mat4f orthogonalMatrix()
98     {
99         if( this.projectionDirty )
100         {
101             updatePerspective();
102             updateOrthogonal();
103             updateProjectionDirty();
104         }
105 
106         return _orthogonalMatrix;
107     }
108 
109     /**
110      * TODO
111      *
112      * Returns:
113      */
114     final mat4f inverseOrthogonalMatrix()
115     {
116         if( this.projectionDirty )
117         {
118             updatePerspective();
119             updateOrthogonal();
120             updateProjectionDirty();
121         }
122 
123         return _inverseOrthogonalMatrix;
124     }
125 
126     /**
127      * TODO
128      *
129      * Returns:
130      */
131     final void updateViewMatrix()
132     {
133         //Assuming pitch & yaw are in radians
134         vec3f eulers = owner.transform.rotation.toEulerAngles();
135         float cosPitch = cos( eulers.x );
136         float sinPitch = sin( eulers.x );
137         float cosYaw = cos( eulers.y );
138         float sinYaw = sin( eulers.y );
139 
140         vec3f xaxis = vec3f( cosYaw, 0.0f, -sinYaw );
141         vec3f yaxis = vec3f( sinYaw * sinPitch, cosPitch, cosYaw * sinPitch );
142         vec3f zaxis = vec3f( sinYaw * cosPitch, -sinPitch, cosPitch * cosYaw );
143 
144         _viewMatrix.clear( 0.0f );
145         _viewMatrix[ 0 ] = vec4f( xaxis, -xaxis.dot( owner.transform.position ) ).vector;
146         _viewMatrix[ 1 ] = vec4f( yaxis, -yaxis.dot( owner.transform.position ) ).vector;
147         _viewMatrix[ 2 ] = vec4f( zaxis, -zaxis.dot( owner.transform.position ) ).vector;
148         _viewMatrix[ 3 ] = vec4f( 0, 0, 0, 1 ).vector;
149 
150         _inverseViewMatrix = _viewMatrix.inverse();
151     }
152 
153     /**
154      * Creates a view matrix looking at a position.
155      *
156      * Params:
157      *  targetPos = The position for the camera to look at.
158      *  cameraPos = The camera's position.
159      *  worldUp = The up direction in the world.
160      *
161      * Returns:
162      * A right handed view matrix for the given params.
163      */
164     final static mat4f lookAt( vec3f targetPos, vec3f cameraPos, vec3f worldUp = vec3f(0,1,0) )
165     {
166         vec3f zaxis = ( cameraPos - targetPos );
167         zaxis.normalize;
168         vec3f xaxis = cross( worldUp, zaxis );
169         xaxis.normalize;
170         vec3f yaxis = cross( zaxis, xaxis );
171 
172         mat4f result = mat4f.identity;
173 
174         result[0][0] = xaxis.x;
175         result[1][0] = xaxis.y;
176         result[2][0] = xaxis.z;
177         result[3][0] = -dot( xaxis, cameraPos );
178         result[0][1] = yaxis.x;
179         result[1][1] = yaxis.y;
180         result[2][1] = yaxis.z;
181         result[3][1] = -dot( yaxis, cameraPos );
182         result[0][2] = zaxis.x;
183         result[1][2] = zaxis.y;
184         result[2][2] = zaxis.z;
185         result[3][2] = -dot( zaxis, cameraPos );
186 
187         return result.transposed;
188     }
189 
190     /**
191      * TODO
192      *
193      * Params:
194      *
195      * Returns:
196      */
197     final override @property bool isDirty()
198     {
199         auto result = owner.transform.matrix != _prevLocalMatrix;
200 
201         _prevLocalMatrix = owner.transform.matrix;
202 
203         return result;
204     }
205 
206 private:
207 
208     /*
209      * Returns whether any of the variables necessary for the projection matrices have changed
210      */
211     final bool projectionDirty()
212     {
213         return fov != _prevFov ||
214             far != _prevFar ||
215             near != _prevNear ||
216             cast(float)Graphics.width != _prevWidth ||
217             cast(float)Graphics.height != _prevHeight;
218     }
219 
220     /*
221      * Updates the projection constants, perspective matrix, and inverse perspective matrix
222      */
223     final void updatePerspective()
224     {
225         _projectionConstants = vec2f( ( -far * near ) / ( far - near ), far / ( far - near ) );
226         _perspectiveMatrix = perspectiveMat( cast(float)Graphics.width, cast(float)Graphics.height, fov, near, far );
227         _inversePerspectiveMatrix = _perspectiveMatrix.inverse();
228     }
229 
230     /*
231      * Updates the orthogonal matrix, and inverse orthogonal matrix
232      */
233     final void updateOrthogonal()
234     {
235         _orthogonalMatrix = mat4f.identity;
236 
237         _orthogonalMatrix[0][0] = 2.0f / Graphics.width;
238         _orthogonalMatrix[1][1] = 2.0f / Graphics.height;
239         _orthogonalMatrix[2][2] = -2.0f / (far - near);
240         _orthogonalMatrix[3][3] = 1.0f;
241 
242         _inverseOrthogonalMatrix = _orthogonalMatrix.inverse();
243     }
244 
245     /*
246      * Sets the _prev values for the projection variables
247      */
248     final void updateProjectionDirty()
249     {
250         _prevFov = fov;
251         _prevFar = far;
252         _prevNear = near;
253         _prevWidth = cast(float)Graphics.width;
254         _prevHeight = cast(float)Graphics.height;
255     }
256 }