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 }