]> git.tdb.fi Git - libs/gl.git/blob - source/matrix.cpp
caa728d0c366708403f31a0776cb85012253e774
[libs/gl.git] / source / matrix.cpp
1 /* $Id$
2
3 This file is part of libmspgl
4 Copyright © 2007  Mikko Rasa, Mikkosoft Productions
5 Distributed under the LGPL
6 */
7
8 #include <algorithm>
9 #include <cmath>
10 #include <msp/core/except.h>
11 #include "matrix.h"
12
13 using namespace std;
14
15 namespace Msp {
16 namespace GL {
17
18 Matrix::Matrix():
19         flags(IDENTITY)
20 {
21         for(unsigned i=0; i<16; ++i)
22                 matrix[i] = (i%5 ? 0 : 1);
23 }
24
25 Matrix::Matrix(const float *m):
26         flags(IDENTITY)
27 {
28         copy(m, m+16, matrix);
29         check_flags();
30 }
31
32 Matrix::Matrix(const double *m):
33         flags(IDENTITY)
34 {
35         copy(m, m+16, matrix);
36         check_flags();
37 }
38
39 void Matrix::check_flags()
40 {
41         const double *m = matrix;
42         if(m[12]!=0 || m[13]!=0 || m[14]!=0)
43                 flags |= TRANSLATE;
44         if(m[0]!=1)
45         {
46                 flags |= SCALE;
47                 if(m[5]!=m[0] || m[10]!=m[0])
48                         flags |= ARBITARY;
49         }
50         if(m[1]!=0 || m[2]!=0 || m[4]!=0 || m[6]!=0 || m[8]!=0 || m[9]!=0)
51         {
52                 flags |= ROTATE;
53                 double x_dot_y = m[0]*m[1]+m[4]*m[5]+m[8]*m[9];
54                 double x_dot_z = m[0]*m[2]+m[4]*m[6]+m[8]*m[10];
55                 double y_dot_z = m[1]*m[2]+m[5]*m[6]+m[9]*m[10];
56                 if(x_dot_y!=0 || x_dot_z!=0 || y_dot_z!=0)
57                         flags |= ARBITARY;
58         }
59         if(m[3]!=0 || m[7]!=0 || m[11]!=0 || m[15]!=1)
60                 flags |= ARBITARY;
61 }
62
63 void Matrix::multiply(const Matrix &other)
64 {
65         *this = *this*other;
66 }
67
68 void Matrix::translate(double x, double y, double z)
69 {
70         multiply(translation(x, y, z));
71 }
72
73 void Matrix::rotate(double a, double x, double y, double z)
74 {
75         multiply(rotation(a, x, y, z));
76 }
77
78 void Matrix::rotate_deg(double a, double x, double y, double z)
79 {
80         multiply(rotation_deg(a, x, y, z));
81 }
82
83 void Matrix::scale(double s)
84 {
85         multiply(scaling(s));
86 }
87
88 void Matrix::scale(double x, double y, double z)
89 {
90         multiply(scaling(x, y, z));
91 }
92
93 Matrix Matrix::operator*(const Matrix &other) const
94 {
95         if(flags==IDENTITY)
96                 return other;
97         else if(other.flags==IDENTITY)
98                 return *this;
99         else if(flags==TRANSLATE && !(other.flags&ARBITARY))
100         {
101                 Matrix result = other;
102                 result.matrix[12] += matrix[12];
103                 result.matrix[13] += matrix[13];
104                 result.matrix[14] += matrix[14];
105                 result.flags |= flags;
106                 return result;
107         }
108         else if(!(flags&ARBITARY) && other.flags==TRANSLATE)
109         {
110                 Matrix result = *this;
111                 const double *m = other.matrix;
112                 result.matrix[12] += matrix[0]*m[12]+matrix[4]*m[13]+matrix[8]*m[14];
113                 result.matrix[13] += matrix[1]*m[12]+matrix[5]*m[13]+matrix[9]*m[14];
114                 result.matrix[14] += matrix[2]*m[12]+matrix[6]*m[13]+matrix[10]*m[14];
115                 result.flags |= other.flags;
116                 return result;
117         }
118         else
119         {
120                 Matrix result;
121                 fill(result.matrix, result.matrix+16, 0.0);
122                 for(unsigned i=0; i<4; ++i)
123                         for(unsigned j=0; j<4; ++j)
124                                 for(unsigned k=0; k<4; ++k)
125                                         result.matrix[i+j*4] += matrix[i+k*4]*other.matrix[k+j*4];
126                 result.flags = flags|other.flags;
127                 return result;
128         }
129 }
130
131 Matrix &Matrix::operator*=(const Matrix &other)
132 {
133         multiply(other);
134         return *this;
135 }
136
137 Vector4 Matrix::operator*(const Vector4 &vec) const
138 {
139         if(flags==IDENTITY)
140                 return vec;
141         else if(flags==TRANSLATE)
142                 return Vector4(vec.x+vec.w*matrix[12], vec.y+vec.w*matrix[13], vec.z+vec.w*matrix[14], vec.w);
143         else if(flags==SCALE)
144                 return Vector4(vec.x*matrix[0], vec.y*matrix[5], vec.z*matrix[10], vec.w);
145         else
146         {
147                 Vector4 result;
148                 result.x = vec.x*matrix[0]+vec.y*matrix[4]+vec.z*matrix[8]+vec.w*matrix[12];
149                 result.y = vec.x*matrix[1]+vec.y*matrix[5]+vec.z*matrix[9]+vec.w*matrix[13];
150                 result.z = vec.x*matrix[2]+vec.y*matrix[6]+vec.z*matrix[10]+vec.w*matrix[14];
151                 result.w = vec.x*matrix[3]+vec.y*matrix[7]+vec.z*matrix[11]+vec.w*matrix[15];
152                 return result;
153         }
154 }
155
156 double Matrix::operator[](unsigned i) const
157 {
158         if(i>=16)
159                 throw InvalidParameterValue("Matrix element index out of range");
160         return matrix[i];
161 }
162
163 Matrix Matrix::translation(double x, double y, double z)
164 {
165         Matrix result;
166         result.matrix[12] = x;
167         result.matrix[13] = y;
168         result.matrix[14] = z;
169         result.flags |= TRANSLATE;
170         return result;
171 }
172
173 Matrix Matrix::rotation(double a, double x, double y, double z)
174 {
175         double l = sqrt(x*x+y*y+z*z);
176         x /= l;
177         y /= l;
178         z /= l;
179         double c = cos(a);
180         double s = sin(a);
181
182         // http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_given_an_axis_and_an_angle
183         Matrix result;
184         result.matrix[0] = c+x*x*(1-c);
185         result.matrix[1] = y*x*(1-c)+z*s;
186         result.matrix[2] = z*x*(1-c)-y*s;
187         result.matrix[4] = x*y*(1-c)-z*s;
188         result.matrix[5] = c+y*y*(1-c);
189         result.matrix[6] = z*y*(1-c)+x*s;
190         result.matrix[8] = x*z*(1-c)+y*s;
191         result.matrix[9] = y*z*(1-c)-x*s;
192         result.matrix[10] = c+z*z*(1-c);
193         result.flags |= ROTATE;
194         return result;
195 }
196
197 Matrix Matrix::rotation_deg(double a, double x, double y, double z)
198 {
199         return rotation(a*M_PI/180, x, y, z);
200 }
201
202 Matrix Matrix::scaling(double s)
203 {
204         Matrix result;
205         result.matrix[0] = s;
206         result.matrix[5] = s;
207         result.matrix[10] = s;
208         result.flags |= SCALE;
209         return result;
210 }
211
212 Matrix Matrix::scaling(double x, double y, double z)
213 {
214         Matrix result;
215         result.matrix[0] = x;
216         result.matrix[5] = y;
217         result.matrix[10] = z;
218         result.flags |= SCALE|ARBITARY;
219         return result;
220 }
221
222 Matrix Matrix::ortho(double l, double r, double b, double t, double n, double f)
223 {
224         if(l==r || b==t || n==f)
225                 throw InvalidParameterValue("Orthogonal projection can't have zero dimension");
226
227         Matrix result;
228         result.matrix[0] = 2/(r-l);
229         result.matrix[5] = 2/(t-b);
230         result.matrix[10] = -2/(f-n);
231         result.matrix[12] = -(r+l)/(r-l);
232         result.matrix[13] = -(t+b)/(t-b);
233         result.matrix[14] = -(f+n)/(f-n);
234         result.flags = TRANSLATE|SCALE|ARBITARY;
235         return result;
236 }
237
238 Matrix Matrix::ortho_centered(double w, double h)
239 {
240         return ortho(-w/2, w/2, -h/2, h/2, -1, 1);
241 }
242
243 Matrix Matrix::ortho_bottomleft(double w, double h)
244 {
245         return ortho(0, w, 0, h, -1, 1);
246 }
247
248 Matrix Matrix::ortho_topleft(double w, double h)
249 {
250         return ortho(0, w, h, 0, -1, 1);
251 }
252
253 Matrix Matrix::frustum(double l, double r, double b, double t, double n, double f)
254 {
255         if(l==r || b==t || n<=0 || f<=n)
256                 throw InvalidParameterValue("Invalid frustum parameters");
257
258         Matrix result;
259         result.matrix[0] = 2*n/(r-l);
260         result.matrix[5] = 2*n/(t-b);
261         result.matrix[8] = (r+l)/(r-l);
262         result.matrix[9] = (t+b)/(t-b);
263         result.matrix[10] = -(f+n)/(f-n);
264         result.matrix[11] = -1;
265         result.matrix[14] = -2*f*n/(f-n);
266         result.matrix[15] = 0;
267         result.flags = ARBITARY;
268         return result;
269 }
270
271 Matrix Matrix::frustum_centered(double w, double h, double n, double f)
272 {
273         return frustum(-w/2, w/2, -h/2, h/2, n, f);
274 }
275
276 Matrix Matrix::perspective(double h, double a, double n, double f)
277 {
278         double hh = tan(h/2)*n;
279         return frustum(-hh*a, hh*a, -hh, hh, n, f);
280 }
281
282
283 GLenum MatrixStack::current_mode = GL_MODELVIEW;
284
285 MatrixStack::MatrixStack(GLenum m):
286         mode(m)
287 {
288         matrices.push_back(Matrix());
289 }
290
291 MatrixStack::MatrixStack():
292         mode(0)
293 {
294         matrices.push_back(Matrix());
295 }
296
297 const Matrix &MatrixStack::top() const
298 {
299         return matrices.back();
300 }
301
302 void MatrixStack::load(const Matrix &m)
303 {
304         matrices.back() = m;
305         update();
306 }
307
308 void MatrixStack::multiply(const Matrix &m)
309 {
310         matrices.back() *= m;
311         update();
312 }
313
314 void MatrixStack::push()
315 {
316         matrices.push_back(top());
317 }
318
319 void MatrixStack::pop()
320 {
321         if(matrices.size()==1)
322                 throw InvalidState("Can't pop the last matrix");
323
324         matrices.pop_back();
325         update();
326 }
327
328 void MatrixStack::update()
329 {
330         if(!mode)
331                 return;
332
333         if(mode!=current_mode)
334         {
335                 glMatrixMode(mode);
336                 current_mode = mode;
337         }
338
339         glLoadMatrixd(matrices.back().data());
340 }
341
342 MatrixStack &MatrixStack::operator=(const Matrix &m)
343 {
344         load(m);
345         return *this;
346 }
347
348 MatrixStack &MatrixStack::operator*=(const Matrix &m)
349 {
350         multiply(m);
351         return *this;
352 }
353
354 MatrixStack &MatrixStack::modelview()
355 {
356         static MatrixStack ms(GL_MODELVIEW);
357         return ms;
358 }
359
360 MatrixStack &MatrixStack::projection()
361 {
362         static MatrixStack ms(GL_PROJECTION);
363         return ms;
364 }
365
366
367 // Deprecated stuff
368
369 MatrixStack *active_stack = &MatrixStack::modelview();
370
371 void matrix_mode(MatrixMode m)
372 {
373         if(m==MODELVIEW)
374                 active_stack = &MatrixStack::modelview();
375         else if(m==PROJECTION)
376                 active_stack = &MatrixStack::projection();
377         else
378                 throw InvalidParameterValue("Texture matrices are not supported");
379 }
380
381 void load_identity()
382 {
383         *active_stack = Matrix();
384 }
385
386 void load_matrix(const float *matrix)
387 {
388         *active_stack = Matrix(matrix);
389 }
390
391 void load_matrix(const double *matrix)
392 {
393         *active_stack = Matrix(matrix);
394 }
395
396 void mult_matrix(const float *matrix)
397 {
398         *active_stack *= Matrix(matrix);
399 }
400
401 void mult_matrix(const double *matrix)
402 {
403         *active_stack *= Matrix(matrix);
404 }
405
406 void push_matrix()
407 {
408         active_stack->push();
409 }
410
411 void pop_matrix()
412 {
413         active_stack->pop();
414 }
415
416 void translate(float x, float y, float z)
417 {
418         *active_stack *= Matrix::translation(x, y, z);
419 }
420
421 void rotate(float a, float x, float y, float z)
422 {
423         *active_stack *= Matrix::rotation_deg(a, x, y, z);
424 }
425
426 void scale(float x, float y, float z)
427 {
428         *active_stack *= Matrix::scaling(x, y, z);
429 }
430
431 void scale_uniform(float s)
432 {
433         *active_stack *= Matrix::scaling(s);
434 }
435
436 } // namespace GL
437 } // namespace Msp