1 module polyplex.math.glmath.matrix; 2 import polyplex.math.glmath.vector; 3 import polyplex.math.glmath.quaternion; 4 import std.conv; 5 import std.stdio; 6 7 struct Matrix(int Dimensions) { 8 alias GMatrix = typeof(this); 9 float[Dimensions][Dimensions] data; 10 11 this(float[Dimensions][Dimensions] d) { 12 this.data = d; 13 } 14 15 private static float[Dimensions][Dimensions] clear(float val) { 16 float[Dimensions][Dimensions] dat; 17 foreach ( x; 0 .. Dimensions ) { 18 foreach ( y; 0 .. Dimensions ) { 19 dat[x][y] = val; 20 } 21 } 22 return dat; 23 } 24 25 public static GMatrix Identity() { 26 float[Dimensions][Dimensions] data = clear(0); 27 foreach ( i; 0 .. Dimensions ) { 28 data[i][i] = 1f; 29 } 30 return GMatrix(data); 31 } 32 33 static if (Dimensions == 4) 34 public static GMatrix Scaling(float3 scale_vec) { 35 GMatrix i = GMatrix.Identity; 36 i.data[0][0] = scale_vec.X; 37 i.data[1][1] = scale_vec.Y; 38 i.data[2][2] = scale_vec.Z; 39 return i; 40 } 41 42 static if(Dimensions == 3) 43 public static GMatrix Scaling(Vector!(float, Dimensions-1) scale_vec) { 44 float[Dimensions][Dimensions] dims = GMatrix.Identity.data; 45 foreach( x; 0 .. Dimensions-1 ) { 46 foreach( y; 0 .. Dimensions-1 ) { 47 if (x == y) dims[x][y] = scale_vec.data[x]; 48 else dims[x][y] = 0; 49 } 50 } 51 dims[Dimensions-1][Dimensions-1] = 1; 52 return GMatrix(dims); 53 } 54 55 static if(Dimensions >= 3) 56 public static GMatrix RotationX(float x_rot) { 57 float[Dimensions][Dimensions] dims = GMatrix.Identity.data; 58 dims[1][1] = Mathf.Cos(x_rot); 59 dims[1][2] = Mathf.Sin(x_rot); 60 dims[2][1] = -Mathf.Sin(x_rot); 61 dims[2][2] = Mathf.Cos(x_rot); 62 return GMatrix(dims); 63 } 64 65 static if(Dimensions >= 3) 66 public static GMatrix RotationY(float y_rot) { 67 float[Dimensions][Dimensions] dims = GMatrix.Identity.data; 68 dims[0][0] = Mathf.Cos(y_rot); 69 dims[0][2] = -Mathf.Sin(y_rot); 70 dims[2][0] = Mathf.Sin(y_rot); 71 dims[2][2] = Mathf.Cos(y_rot); 72 return GMatrix(dims); 73 } 74 75 static if(Dimensions >= 3) 76 public static GMatrix RotationZ(float z_rot) { 77 float[Dimensions][Dimensions] dims = GMatrix.Identity.data; 78 dims[0][0] = Mathf.Cos(z_rot); 79 dims[1][0] = Mathf.Sin(z_rot); 80 dims[0][1] = -Mathf.Sin(z_rot); 81 dims[1][1] = Mathf.Cos(z_rot); 82 return GMatrix(dims); 83 } 84 85 static if (Dimensions == 2) 86 public static GMatrix Rotation(float rot) { 87 return GMatrix([ 88 [Mathf.Cos(rot), -Mathf.Sin(rot)], 89 [Mathf.Sin(rot), Mathf.Cos(rot)] 90 ]); 91 } 92 93 static if (Dimensions == 4) 94 public static GMatrix FromQuaternion(Quaternion quat) { 95 float qx = quat.X; 96 float qy = quat.Y; 97 float qz = quat.Z; 98 float qw = quat.w; 99 float n = 2f/(qx*qx+qy*qy+qz*qz+qw*qw); 100 qx *= n; 101 qy *= n; 102 qz *= n; 103 qw *= n; 104 return GMatrix([ 105 [1.0f - n*qy*qy - n*qz*qz, n*qx*qy - n*qz*qw, n*qx*qz + n*qy*qw, 0f], 106 [n*qx*qy + n*qz*qw, 1f - n*qx*qx - n*qz*qz, n*qy*qz - n*qx*qw, 0f], 107 [n*qx*qz - n*qy*qw, n*qy*qz + n*qx*qw, 1f - n*qx*qx - n*qy*qy, 0f], 108 [0f, 0f, 0f, 1f] 109 ]); 110 } 111 112 static if(Dimensions == 2) 113 public static GMatrix Translation(Vector!(float, Dimensions) trans_vec) { 114 return GMatrix([ 115 [1f, trans_vec.X], 116 [0f, trans_vec.Y] 117 ]); 118 } 119 120 static if(Dimensions == 3) 121 public static GMatrix Translation(Vector!(float, Dimensions) trans_vec) { 122 return GMatrix([ 123 [1f, 0f, trans_vec.X], 124 [0f, 1f, trans_vec.Y], 125 [0f, 0f, trans_vec.Z] 126 ]); 127 } 128 129 static if(Dimensions == 4) 130 public static GMatrix Translation(Vector!(float, Dimensions-1) trans_vec) { 131 return GMatrix([ 132 [1f, 0f, 0f, trans_vec.X], 133 [0f, 1f, 0f, trans_vec.Y], 134 [0f, 0f, 1f, trans_vec.Z], 135 [0f, 0f, 0f, 1f] 136 ]); 137 } 138 139 static if(Dimensions == 4) 140 public static GMatrix Orthographic(float left, float right, float bottom, float top, float znear, float zfar) { 141 return GMatrix([ 142 [2/(right-left), 0f, 0f, -(right+left)/(right-left)], 143 [0f, 2/(top-bottom), 0f, -(top+bottom)/(top-bottom)], 144 [0f, 0f, -2/(zfar-znear), -(zfar+znear)/(zfar-znear)], 145 [0f, 0f, 0f, 1f] 146 ]); 147 } 148 149 static if(Dimensions == 4) 150 public static GMatrix OrthographicInverse(float left, float right, float bottom, float top, float znear, float zfar) { 151 return GMatrix([ 152 [(right-left)/2f, 0, 0, 0], 153 [0, (top-bottom)/2f, 0, 0], 154 [0, 0, (zfar-znear)/-2f, 0], 155 [(right+left)/2f, (top+bottom)/2f, (zfar+znear)/2f, 1f] 156 ]); 157 } 158 159 private static float[6] prepPerspective(float width, float height, float fov, float znear, float zfar) { 160 float aspect = width/height; 161 float top = znear * Mathf.Tan(Mathf.ToRadians(fov)); 162 float bottom = -top; 163 float right = top * aspect; 164 float left = -right; 165 return [left, right, bottom, top, znear, zfar]; 166 } 167 168 static if(Dimensions == 4) 169 public static GMatrix Perspective(float width, float height, float fov, float znear, float zfar) { 170 float[6] persp_data = prepPerspective(width, height, fov, znear, zfar); 171 return perspective(persp_data[0], persp_data[1], persp_data[2], persp_data[3], persp_data[4], persp_data[5]); 172 } 173 174 static if(Dimensions == 4) 175 private static GMatrix perspective(float left, float right, float bottom, float top, float near, float far) { 176 return GMatrix([ 177 [(2f*near)/(right-left), 0, (right+left)/(right-left), 0], 178 [0, (2f*near)/(top-bottom), -(2f*far*near)/(far-near), 0], 179 [0, (top+bottom)/(top-bottom), -(far+near)/(far-near), 0], 180 [0, 0, -1f, 0] 181 ]); 182 } 183 184 public GMatrix opBinary(string op: "*")(GMatrix other) { 185 float[Dimensions][Dimensions] dim = clear(0); 186 foreach ( row; 0 .. Dimensions ) { 187 foreach ( column; 0 .. Dimensions ) { 188 foreach ( i; 0 .. Dimensions ) { 189 dim[row][column] += this.data[row][i] * other.data[i][column]; 190 } 191 } 192 } 193 return GMatrix(dim); 194 } 195 196 public GMatrix Transpose() pure const nothrow { 197 GMatrix temp = this; 198 static foreach( x; 0 .. Dimensions ) { 199 static foreach ( y; 0 .. Dimensions ) { 200 temp.data[x][y] = this.data[y][x]; 201 } 202 } 203 return temp; 204 } 205 206 static if (Dimensions == 4) 207 public GMatrix Translate (Vector!(float, Dimensions-1) trans_vec) { 208 this = GMatrix.Translation(trans_vec) * this; 209 return this; 210 } 211 212 static if (Dimensions >= 3) 213 public GMatrix Scale(Vector!(float, Dimensions-1) scale_vec) { 214 this = GMatrix.Scaling(scale_vec) * this; 215 return this; 216 } 217 218 static if (Dimensions >= 3) 219 public GMatrix RotateX(float rot) { 220 this = GMatrix.RotationX(rot) * this; 221 return this; 222 } 223 224 static if (Dimensions >= 3) 225 public GMatrix RotateY(float rot) { 226 return GMatrix.RotationY(rot) * this; 227 } 228 229 static if (Dimensions >= 3) 230 public GMatrix RotateZ(float rot) { 231 return GMatrix.RotationZ(rot) * this; 232 } 233 234 static if (Dimensions == 2) 235 public GMatrix Rotate(float rot) { 236 return GMatrix.Rotation(rot) * this; 237 } 238 239 static if(Dimensions == 4) 240 public GMatrix Rotate(Quaternion rot) { 241 return GMatrix.FromQuaternion(rot) * this; 242 } 243 244 public string ToString() { 245 string dim = "["; 246 foreach( x; 0 .. Dimensions ) { 247 dim ~= "\n\t["; 248 foreach ( y; 0 .. Dimensions ) { 249 dim ~= this.data[x][y].text; 250 if (y != Dimensions-1) dim ~= ", "; 251 } 252 dim ~= "]"; 253 if (x != Dimensions-1) dim ~= ","; 254 } 255 return dim ~ "\n]"; 256 } 257 258 /// Returns a pointer to the data container of this vector 259 public inout(float)* ptr() pure inout nothrow { return data[0].ptr; } 260 } 261 262 alias Matrix4x4 = Matrix!4; 263 alias Matrix3x3 = Matrix!3; 264 alias Matrix2x2 = Matrix!2; 265 266 alias mat2x2 = Matrix2x2; 267 alias mat3x3 = Matrix3x3; 268 alias mat4x4 = Matrix4x4; 269 270 unittest { 271 Matrix4x4 mt4 = Matrix4x4.Identity; 272 Matrix4x4 mt4_o = Matrix4x4([ 273 [1, 0, 0, 2], 274 [0, 1, 0, 5], 275 [0, 0, 1, 3], 276 [0, 0, 0, 1] 277 ]); 278 279 assert((mt4*mt4_o) == mt4_o, "Matrix multiplication error! should be " ~ mt4_o.ToString); 280 281 Matrix3x3 mt3 = Matrix3x3.Identity; 282 Matrix3x3 mt3_o = Matrix3x3([ 283 [1, 0, 3], 284 [0, 1, 2], 285 [0, 0, 1] 286 ]); 287 assert((mt3*mt3_o) == mt3_o, "Matrix multiplication error! should be " ~ mt3_o.ToString); 288 289 Matrix2x2 mt2 = Matrix2x2.Identity; 290 Matrix2x2 mt2_o = Matrix2x2([ 291 [1, 2], 292 [0, 1], 293 ]); 294 assert((mt2*mt2_o) == mt2_o, "Matrix multiplication error! should be " ~ mt3_o.ToString); 295 296 Matrix4x4 orth_mat = Matrix4x4.Orthographic(-1f, 1f, -1f, 1f, -1f, 1f); 297 Matrix4x4 comp_orth = Matrix4x4([ 298 [1f, 0f, 0f, -0f], 299 [0f, 1f, 0f, -0f], 300 [0f, 0f, -1f, -0f], 301 [0f, 0f, 0f, 1f] 302 ]); 303 assert(orth_mat == comp_orth, "orth_mat != comp_orth \n" ~ orth_mat.ToString ~ ",\n" ~ comp_orth.ToString); 304 }