1 /* 2 ******************************************************************************************* 3 * Dgame (a D game framework) - Copyright (c) Randy Schütt 4 * 5 * This software is provided 'as-is', without any express or implied warranty. 6 * In no event will the authors be held liable for any damages arising from 7 * the use of this software. 8 * 9 * Permission is granted to anyone to use this software for any purpose, 10 * including commercial applications, and to alter it and redistribute it 11 * freely, subject to the following restrictions: 12 * 13 * 1. The origin of this software must not be misrepresented; you must not claim 14 * that you wrote the original software. If you use this software in a product, 15 * an acknowledgment in the product documentation would be appreciated but is 16 * not required. 17 * 18 * 2. Altered source versions must be plainly marked as such, and must not be 19 * misrepresented as being the original software. 20 * 21 * 3. This notice may not be removed or altered from any source distribution. 22 ******************************************************************************************* 23 */ 24 module Dgame.Math.Matrix4x4; 25 26 private: 27 28 static import std.math; 29 30 import Dgame.Math.Vector3; 31 import Dgame.Math.Vector2; 32 import Dgame.Math.Rect; 33 34 @nogc 35 bool feq(float a, float b) pure nothrow { 36 return std.math.fabs(a - b) > float.epsilon; 37 } 38 39 @nogc 40 ref Matrix4x4 merge(ref Matrix4x4 lhs, ref const Matrix4x4 rhs) pure nothrow { 41 lhs = Matrix4x4( 42 lhs[0] * rhs[0] + lhs[4] * rhs[1] + lhs[12] * rhs[3], 43 lhs[0] * rhs[4] + lhs[4] * rhs[5] + lhs[12] * rhs[7], 44 lhs[0] * rhs[12] + lhs[4] * rhs[13] + lhs[12] * rhs[15], 45 lhs[1] * rhs[0] + lhs[5] * rhs[1] + lhs[13] * rhs[3], 46 lhs[1] * rhs[4] + lhs[5] * rhs[5] + lhs[13] * rhs[7], 47 lhs[1] * rhs[12] + lhs[5] * rhs[13] + lhs[13] * rhs[15], 48 lhs[3] * rhs[0] + lhs[7] * rhs[1] + lhs[15] * rhs[3], 49 lhs[3] * rhs[4] + lhs[7] * rhs[5] + lhs[15] * rhs[7], 50 lhs[3] * rhs[12] + lhs[7] * rhs[13] + lhs[15] * rhs[15]); 51 52 return lhs; 53 } 54 55 public: 56 57 /** 58 * A Matrix is a structure which may describe different transformation. 59 * Note: Matrix4x4.init is the identity Matrix. 60 * 61 * Author: Randy Schuett (rswhite4@googlemail.com) 62 */ 63 struct Matrix4x4 { 64 private: 65 float[16] _values = [ 66 1, 0, 0, 0, 67 0, 1, 0, 0, 68 0, 0, 1, 0, 69 0, 0, 0, 1 70 ]; 71 72 public: 73 /** 74 * CTor 75 */ 76 @nogc 77 this(float a, float b, float c, 78 float d, float e, float f, 79 float g, float h, float i) pure nothrow 80 { 81 _values[0] = a; _values[4] = b; _values[8] = 0; _values[12] = c; 82 _values[1] = d; _values[5] = e; _values[9] = 0; _values[13] = f; 83 _values[2] = 0; _values[6] = 0; _values[10] = 1; _values[14] = 0; 84 _values[3] = g; _values[7] = h; _values[11] = 0; _values[15] = i; 85 } 86 87 /** 88 * Returns the inverse Matrix of the current. 89 * If the current matrix has a determinant of approximately zero, the identity Matrix (.init) is returned. 90 */ 91 @nogc 92 Matrix4x4 getInverse() const pure nothrow { 93 immutable float my_det = this.det(); 94 95 if (!feq(my_det, 0)) { 96 return Matrix4x4((_values[15] * _values[5] - _values[7] * _values[13]) / my_det, 97 -(_values[15] * _values[4] - _values[7] * _values[12]) / my_det, 98 (_values[13] * _values[4] - _values[5] * _values[12]) / my_det, 99 -(_values[15] * _values[1] - _values[3] * _values[13]) / my_det, 100 (_values[15] * _values[0] - _values[3] * _values[12]) / my_det, 101 -(_values[13] * _values[0] - _values[1] * _values[12]) / my_det, 102 (_values[7] * _values[1] - _values[3] * _values[5]) / my_det, 103 -(_values[7] * _values[0] - _values[3] * _values[4]) / my_det, 104 (_values[5] * _values[0] - _values[1] * _values[4]) / my_det); 105 } 106 107 return Matrix4x4.init; 108 } 109 110 /** 111 * Reset the current Matrix to the identity Matrix 112 */ 113 @nogc 114 ref Matrix4x4 loadIdentity() pure nothrow { 115 _values[0] = 1.0f; _values[4] = 0.0f; _values[8] = 0.0f; _values[12] = 0.0f; 116 _values[1] = 0.0f; _values[5] = 1.0f; _values[9] = 0.0f; _values[13] = 0.0f; 117 _values[2] = 0.0f; _values[6] = 0.0f; _values[10] = 1.0f; _values[14] = 0.0f; 118 _values[3] = 0.0f; _values[7] = 0.0f; _values[11] = 0.0f; _values[15] = 1.0f; 119 120 return this; 121 } 122 123 /** 124 * Calculate the determinant 125 */ 126 @nogc 127 float det() const pure nothrow { 128 return _values[0] * (_values[15] * _values[5] - _values[7] * _values[13]) - 129 _values[1] * (_values[15] * _values[4] - _values[7] * _values[12]) + 130 _values[3] * (_values[13] * _values[4] - _values[5] * _values[12]); 131 } 132 133 /** 134 * Translate the Matrix 135 */ 136 @nogc 137 ref Matrix4x4 translate(const Vector2f vec) pure nothrow { 138 const Matrix4x4 translation = Matrix4x4(1, 0, vec.x, 139 0, 1, vec.y, 140 0, 0, 1); 141 return merge(this, translation); 142 } 143 144 /** 145 * Rotate the Matrix about angle (in degree!) 146 */ 147 @nogc 148 ref Matrix4x4 rotate(float angle) pure nothrow { 149 immutable float rad = angle * std.math.PI / 180f; 150 immutable float cos = std.math.cos(rad); 151 immutable float sin = std.math.sin(rad); 152 153 const Matrix4x4 rotation = Matrix4x4(cos, -sin, 0, 154 sin, cos, 0, 155 0, 0, 1); 156 return merge(this, rotation); 157 } 158 159 /** 160 * Rotate the Matrix about angle (in degree!) about the given center position 161 */ 162 @nogc 163 ref Matrix4x4 rotate(float angle, const Vector2f center) pure nothrow { 164 immutable float rad = angle * std.math.PI / 180f; 165 immutable float cos = std.math.cos(rad); 166 immutable float sin = std.math.sin(rad); 167 168 const Matrix4x4 rotation = Matrix4x4(cos, -sin, center.x * (1 - cos) + center.y * sin, 169 sin, cos, center.y * (1 - cos) - center.x * sin, 170 0, 0, 1); 171 return merge(this, rotation); 172 } 173 174 /** 175 * Scale the Matrix about factor scale 176 */ 177 @nogc 178 ref Matrix4x4 scale(const Vector2f scale) pure nothrow { 179 const Matrix4x4 scaling = Matrix4x4(scale.x, 0, 0, 180 0, scale.y, 0, 181 0, 0, 1); 182 183 return merge(this, scaling); 184 } 185 186 /** 187 * Scale the Matrix about factor scale about the given center position 188 */ 189 @nogc 190 ref Matrix4x4 scale(const Vector2f scale, const Vector2f center) pure nothrow { 191 const Matrix4x4 scaling = Matrix4x4(scale.x, 0, center.x * (1 - scale.x), 192 0, scale.y, center.y * (1 - scale.y), 193 0, 0, 1); 194 return merge(this, scaling); 195 } 196 197 /** 198 * Calculates a View-Matrix 199 * 200 * See: <a href="http://3dgep.com/understanding-the-view-matrix/#Look_At_Camera">here</a> 201 */ 202 @nogc 203 void lookAt(const Vector3f eye, const Vector3f look, const Vector3f up) pure nothrow { 204 const Vector3f dir = (look - eye).normalize(); 205 const Vector3f right = dir.cross(up).normalize(); 206 const Vector3f up2 = right.cross(dir).normalize(); 207 208 Matrix4x4 mat; 209 mat[0] = right.x; 210 mat[4] = right.y; 211 mat[8] = right.z; 212 mat[12] = -right.dot(eye); 213 214 mat[1] = up2.x; 215 mat[5] = up2.y; 216 mat[9] = up2.z; 217 mat[13] = -up2.dot(eye); 218 219 mat[2] = -dir.x; 220 mat[6] = -dir.y; 221 mat[10] = -dir.z; 222 mat[14] = dir.dot(eye); 223 224 mat[3] = 0; 225 mat[7] = 0; 226 mat[11] = 0; 227 mat[15] = 1; 228 229 merge(this, mat); 230 } 231 232 /** 233 * Calculate a perspective projection 234 * 235 * See: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">here</a> 236 */ 237 @nogc 238 void perspective(float fov, float ratio, float nearp, float farp) pure nothrow { 239 immutable float f = 1f / std.math.tan(fov * (std.math.PI / 360f)); 240 241 Matrix4x4 mat; 242 mat[0] = f / ratio; 243 mat[5] = f; 244 mat[10] = (farp + nearp) / (nearp - farp); 245 mat[11] = -1; 246 mat[14] = (2 * farp * nearp) / (nearp - farp); 247 mat[15] = 0; 248 249 merge(this, mat); 250 } 251 252 /** 253 * Calculate a prthographic projection 254 * 255 * See: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">here</a> 256 */ 257 @nogc 258 bool ortho(const Rect rect, float zNear = 1, float zFar = -1) pure nothrow { 259 if (!rect.isEmpty()) { 260 immutable float inv_z = 1.0 / (zFar - zNear); 261 immutable float inv_y = 1.0 / (cast(float) rect.x - rect.height); 262 immutable float inv_x = 1.0 / (cast(float) rect.width - rect.y); 263 264 Matrix4x4 mat; 265 // first column 266 mat[0] = 2.0 * inv_x; 267 // second 268 mat[5] = 2.0 * inv_y; 269 // third 270 mat[10] = -2.0 * inv_z; 271 // fourth 272 mat[12] = -(cast(float) rect.width + rect.y) * inv_x; 273 mat[13] = -(cast(float) rect.x + rect.height) * inv_y; 274 mat[14] = -(zFar + zNear) * inv_z; 275 276 merge(this, mat); 277 278 return true; 279 } 280 281 return false; 282 } 283 284 /** 285 * Returns the 16 values of the Matrix by ref 286 */ 287 @nogc 288 ref inout(float[16]) getValues() inout pure nothrow { 289 return _values; 290 } 291 292 /** 293 * Returns a specific value by index 294 */ 295 @nogc 296 ref inout(float) opIndex(ubyte index) inout pure nothrow { 297 return _values[index]; 298 } 299 300 /** 301 * Supported operations: only * 302 */ 303 @nogc 304 Matrix4x4 opBinary(string op : "*")(ref const Matrix4x4 mat) const pure nothrow { 305 Matrix4x4 cpy = this; 306 merge(cpy, mat); 307 308 return cpy; 309 } 310 311 /** 312 * Supported operations: only *= 313 */ 314 @nogc 315 ref Matrix4x4 opOpAssign(string op : "*")(ref const Matrix4x4 math) pure nothrow { 316 return merge(this, math); 317 } 318 319 /** 320 * Compares two Matrices approximately 321 */ 322 @nogc 323 bool opEquals(ref const Matrix4x4 mat) const pure nothrow { 324 for (ubyte i = 0; i < 16; i++) { 325 if (!feq(mat[i], _values[i])) 326 return false; 327 } 328 329 return true; 330 } 331 }