1 /**
2  * Copyright Alex Parker 2013-2014
3  *
4  * This file contains vec3, mat4 data structures and associated operations.
5  **/
6 
7 module meld.maths;
8 import std.math;
9 
10 //converts radians to degrees
11 float rad2deg(float radians)
12 {
13     return radians * (180.0f/PI);
14 }
15 
16 //converts degrees to radians
17 float deg2rad(float deg)
18 {
19     return (deg * PI) / 180.0f;
20 }
21 
22 //represents a 2D vector
23 struct vec2
24 {
25     static immutable vec2 zero = vec2(0.0f, 0.0f);
26     float x, y;
27     
28     this(float x, float y)
29     {
30         this.x = x;
31         this.y = y;
32     }
33     
34 	vec2 opUnary(string op)()
35 	{
36 		static if (op == "-")
37 			return vec2(-x, -y);
38 	}
39 
40     //multiplies the vector by a scalar, returning the result.
41     vec2 opBinary(string op)(immutable float scalar)
42     {
43         static if (op == "*")
44             return vec2(x * scalar, y * scalar);
45         else static assert(0, "Operator "~op~" not implemented");
46     }
47 
48     vec2 opBinary(string op)(immutable vec2 other)
49     {
50         static if (op == "+")
51             return vec2(x + other.x, y + other.y);
52         else static if (op == "-")
53             return vec2(x - other.x, y - other.y);
54         else static assert(0, "Operator "~op~" not implemented");
55     }
56 
57     //calculate the dot product with the other vector, returning the result.
58     float dot(immutable vec2 other)
59     {
60         return x*other.x + y*other.y;
61     }
62     
63     //calculates the squared length of the current vector.
64     float lengthSq()
65     {
66         return dot(this);
67     }
68     
69 	//calculates the length of the vector.
70     float length()
71     {
72         return sqrt(lengthSq());
73     }
74     
75     //normalizes the current vector.
76     void normalize()
77     {
78         float len = length();
79         x /= len;
80         y /= len;
81     }
82 };
83 
84 //represents a 3D vector
85 struct vec3
86 {
87     static immutable vec3 zero = vec3(0.0f, 0.0f, 0.0f);
88     static immutable vec3 forward = vec3(0.0f, 0.0f, 1.0f);
89     static immutable vec3 right = vec3(1.0f, 0.0f, 0.0f);
90     static immutable vec3 up = vec3(0.0f, 1.0f, 0.0f);
91 
92     float x, y, z;
93     
94     this(float x, float y, float z)
95     {
96         this.x = x;
97         this.y = y;
98         this.z = z;
99     }
100     
101 	vec3 opUnary(string op)()
102 	{
103         return mixin("vec3("~op~"x, "~op~"y, "~op~"z)");
104 	}
105 
106 	//multiplies the vector by a scalar, returning the result.
107     vec3 opBinary(string op)(immutable float scalar)
108     {
109         return mixin("vec3(x "~op~" scalar, y "~op~" scalar, z "~op~" scalar)");
110     }
111 	
112     vec3 opBinary(string op)(immutable vec3 other)
113     {
114         return mixin("vec3(x "~op~" other.x, y "~op~" other.y, z "~op~" other.z)");
115     }
116     
117 	//calculate the dot product with the other vector, returning the result.
118     static float dot(immutable vec3 a, immutable vec3 b)
119     {
120         return a.x*b.x + a.y*b.y + b.z*b.z;
121     }
122     
123 	//calculates the squared length of the current vector.
124     @property float lengthSq()
125     {
126         return vec3.dot(this, this);
127     }
128     
129 	//calculates the length of the vector.
130     @property float length()
131     {
132         return sqrt(lengthSq());
133     }
134     
135 	//normalizes a copy of the vector
136     vec3 normalized()
137     {
138         float len = length();
139         return vec3(x / len, y / len, z / len);
140     }
141 
142     //calculates the cross product with the other vector, returning the result.
143     static vec3 cross(immutable vec3 a, immutable vec3 b)
144     {
145         return vec3(
146 	        a.y*b.z - a.z*b.y,
147 	        a.z*b.x - a.x*b.z,
148 	        a.x*b.y - a.y*b.x
149         );
150     }
151 };
152 
153 //represents a 4D vector
154 struct vec4
155 {
156 	float x, y, z, w;
157 
158 	this(float x, float y, float z, float w)
159 	{
160 		this.x = x;
161 		this.y = y;
162 		this.z = z;
163 		this.w = w;
164 	}
165 
166 	vec3 opCast()
167 	{
168 		return vec3(x, y, z);
169 	}
170 }
171 
172 //represents a 4x4 matrix
173 struct mat4
174 {
175     float rows[16];
176 
177     this(immutable float rows[16])
178     {
179         this.rows = rows;
180     }
181 
182 	//performs a matrix multiplication and returns the result.
183     mat4 opBinary(string op)(immutable mat4 other)
184     {
185         static if (op == "*")
186         {
187             mat4 res;
188             for (int i = 0; i<16; i+=4)
189                 for (int j = 0; j<4; j++)
190                     res.rows[i+j] =
191                     other.rows[i]*rows[j] +
192                     other.rows[i+1]*rows[j+4] +
193                     other.rows[i+2]*rows[j+8] +
194                     other.rows[i+3]*rows[j+12];
195             
196             return res;
197         }
198     }
199 
200 	vec4 opBinary(string op)(immutable vec4 other)
201 	{
202 		static if (op == "*")
203 		{
204 			return vec4(
205 				other.x * rows[0] + other.y * rows[4] + other.z * rows[8] + other.w * rows[12],
206 				other.x * rows[1] + other.y * rows[5] + other.z * rows[9] + other.w * rows[13],
207 				other.x * rows[2] + other.y * rows[6] + other.z * rows[10] + other.w * rows[14],
208 				other.x * rows[3] + other.y * rows[7] + other.z * rows[11] + other.w * rows[15]
209 			);
210 		}
211 	}
212 
213     vec3 opBinary(string op)(immutable vec3 other)
214     {
215         static if (op == "*")
216         {
217             return vec3(
218                 other.x * rows[0] + other.y * rows[4] + other.z * rows[8],
219                 other.x * rows[1] + other.y * rows[5] + other.z * rows[9],
220                 other.x * rows[2] + other.y * rows[6] + other.z * rows[10]
221             );
222         }
223     }
224     
225     //the identity matrix
226     static const mat4 identity = mat4([
227         1.0f, 0.0f, 0.0f, 0.0f,
228         0.0f, 1.0f, 0.0f, 0.0f,
229         0.0f, 0.0f, 1.0f, 0.0f,
230         0.0f, 0.0f, 0.0f, 1.0f
231     ]);
232 
233 	//produces a axis angle matrix. This will produce a rotation in radians about the normalized axis.
234     static mat4 axisangle(immutable vec3 axis, float angle)
235     {
236         float c = cos(angle), ic = 1.0f - c;
237         float s = sin(angle);
238         mat4 mat = mat4([
239             c+ic*axis.x*axis.x,         ic*axis.x*axis.y-axis.z*s,  ic*axis.x*axis.z+axis.y*s, 0.0f,
240             ic*axis.x*axis.y+axis.z*s,  c+ic*axis.y*axis.y,         ic*axis.y*axis.z-axis.x*s, 0.0f,
241             ic*axis.x*axis.z-axis.y*s,  ic*axis.y*axis.z+axis.x*s,  c+ic*axis.z*axis.z,        0.0f,
242             0.0f,                       0.0f,                       0.0f,                      1.0f
243         ]);
244 		return mat;
245     }
246 
247     //produces a translation matrix
248     static mat4 translate(immutable vec3 pos)
249     {
250         return mat4([
251             1.0f, 0.0f, 0.0f, pos.x,
252             0.0f, 1.0f, 0.0f, pos.y,
253             0.0f, 0.0f, 1.0f, pos.z,
254             0.0f, 0.0f, 0.0f, 1.0f
255         ]);
256     }
257 
258     static mat4 basis(immutable vec3 pos, immutable vec3 forward, immutable vec3 up)
259     {
260         vec3 right = vec3.cross(forward, up);
261         return mat4([
262             forward.x, up.x, right.x, pos.x,
263             forward.y, up.y, right.y, pos.y,
264             forward.z, up.z, right.z, pos.z,
265             0.0f,      0.0f, 0.0f,    1.0f
266         ]);
267     }
268     
269 	//calculates a projection matrix from the field of view in radians,
270 	//the aspect ratio, the near culling plane and far culling plane.
271     static mat4 proj(float fov, float aspect, float n, float f)
272     {
273         float yScale = 1.0f/tan(fov*0.5f);
274         float xScale = yScale / aspect;
275         
276         mat4 mat = mat4([
277             xScale, 0.0f,   0.0f, 0.0f,
278             0.0f,   yScale, 0.0f, 0.0f,
279             0.0f,   0.0f,   f/(f-n), (-f*n)/(f-n),
280             0.0f,   0.0f,   1.0f, 0.0f
281         ]);
282 		return mat;
283     }
284 }