]> git.tdb.fi Git - libs/gl.git/commitdiff
Replace GL matrices with an object-oriented implementation
authorMikko Rasa <tdb@tdb.fi>
Tue, 2 Nov 2010 13:10:08 +0000 (13:10 +0000)
committerMikko Rasa <tdb@tdb.fi>
Tue, 2 Nov 2010 13:10:08 +0000 (13:10 +0000)
source/matrix.cpp
source/matrix.h
source/projection.cpp
source/projection.h

index 9af850d2dfa68956037be0e4854716813fdc79a6..d8f018aaf4835814c4736ac389f8e7426dd6279d 100644 (file)
@@ -5,69 +5,413 @@ Copyright © 2007  Mikko Rasa, Mikkosoft Productions
 Distributed under the LGPL
 */
 
+#include <algorithm>
+#include <cmath>
+#include <msp/core/except.h>
 #include "matrix.h"
 
+using namespace std;
+
 namespace Msp {
 namespace GL {
 
+Matrix::Matrix():
+       flags(IDENTITY)
+{
+       for(unsigned i=0; i<16; ++i)
+               matrix[i] = (i%5 ? 0 : 1);
+}
+
+Matrix::Matrix(const float *m):
+       flags(IDENTITY)
+{
+       copy(m, m+16, matrix);
+       check_flags();
+}
+
+Matrix::Matrix(const double *m):
+       flags(IDENTITY)
+{
+       copy(m, m+16, matrix);
+       check_flags();
+}
+
+void Matrix::check_flags()
+{
+       const double *m = matrix;
+       if(m[12]!=0 || m[13]!=0 || m[14]!=0)
+               flags |= TRANSLATE;
+       if(m[0]!=1)
+       {
+               flags |= SCALE;
+               if(m[5]!=m[0] || m[10]!=m[0])
+                       flags |= ARBITARY;
+       }
+       if(m[1]!=0 || m[2]!=0 || m[4]!=0 || m[6]!=0 || m[8]!=0 || m[9]!=0)
+       {
+               flags |= ROTATE;
+               double x_dot_y = m[0]*m[1]+m[4]*m[5]+m[8]*m[9];
+               double x_dot_z = m[0]*m[2]+m[4]*m[6]+m[8]*m[10];
+               double y_dot_z = m[1]*m[2]+m[5]*m[6]+m[9]*m[10];
+               if(x_dot_y!=0 || x_dot_z!=0 || y_dot_z!=0)
+                       flags |= ARBITARY;
+       }
+       if(m[3]!=0 || m[7]!=0 || m[11]!=0 || m[15]!=1)
+               flags |= ARBITARY;
+}
+
+void Matrix::multiply(const Matrix &other)
+{
+       *this = *this*other;
+}
+
+void Matrix::translate(double x, double y, double z)
+{
+       multiply(translation(x, y, z));
+}
+
+void Matrix::rotate(double a, double x, double y, double z)
+{
+       multiply(rotation(a, x, y, z));
+}
+
+void Matrix::rotate_deg(double a, double x, double y, double z)
+{
+       multiply(rotation_deg(a, x, y, z));
+}
+
+void Matrix::scale(double s)
+{
+       multiply(scaling(s));
+}
+
+void Matrix::scale(double x, double y, double z)
+{
+       multiply(scaling(x, y, z));
+}
+
+Matrix Matrix::operator*(const Matrix &other) const
+{
+       if(flags==IDENTITY)
+               return other;
+       else if(other.flags==IDENTITY)
+               return *this;
+       else if(flags==TRANSLATE && !(other.flags&ARBITARY))
+       {
+               Matrix result = other;
+               result.matrix[12] += matrix[12];
+               result.matrix[13] += matrix[13];
+               result.matrix[14] += matrix[14];
+               result.flags |= flags;
+               return result;
+       }
+       else if(!(flags&ARBITARY) && other.flags==TRANSLATE)
+       {
+               Matrix result = *this;
+               const double *m = other.matrix;
+               result.matrix[12] += matrix[0]*m[12]+matrix[4]*m[13]+matrix[8]*m[14];
+               result.matrix[13] += matrix[1]*m[12]+matrix[5]*m[13]+matrix[9]*m[14];
+               result.matrix[14] += matrix[2]*m[12]+matrix[6]*m[13]+matrix[10]*m[14];
+               result.flags |= other.flags;
+               return result;
+       }
+       else
+       {
+               Matrix result;
+               fill(result.matrix, result.matrix+16, 0.0);
+               for(unsigned i=0; i<4; ++i)
+                       for(unsigned j=0; j<4; ++j)
+                               for(unsigned k=0; k<4; ++k)
+                                       result.matrix[i+j*4] += matrix[i+k*4]*other.matrix[k+j*4];
+               result.flags = flags|other.flags;
+               return result;
+       }
+}
+
+Matrix &Matrix::operator*=(const Matrix &other)
+{
+       multiply(other);
+       return *this;
+}
+
+double Matrix::operator[](unsigned i) const
+{
+       if(i>=16)
+               throw InvalidParameterValue("Matrix element index out of range");
+       return matrix[i];
+}
+
+Matrix Matrix::translation(double x, double y, double z)
+{
+       Matrix result;
+       result.matrix[12] = x;
+       result.matrix[13] = y;
+       result.matrix[14] = z;
+       result.flags |= TRANSLATE;
+       return result;
+}
+
+Matrix Matrix::rotation(double a, double x, double y, double z)
+{
+       double l = sqrt(x*x+y*y+z*z);
+       x /= l;
+       y /= l;
+       z /= l;
+       double c = cos(a);
+       double s = sin(a);
+
+       // http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_given_an_axis_and_an_angle
+       Matrix result;
+       result.matrix[0] = c+x*x*(1-c);
+       result.matrix[1] = y*x*(1-c)+z*s;
+       result.matrix[2] = z*x*(1-c)-y*s;
+       result.matrix[4] = x*y*(1-c)-z*s;
+       result.matrix[5] = c+y*y*(1-c);
+       result.matrix[6] = z*y*(1-c)+x*s;
+       result.matrix[8] = x*z*(1-c)+y*s;
+       result.matrix[9] = y*z*(1-c)-x*s;
+       result.matrix[10] = c+z*z*(1-c);
+       result.flags |= ROTATE;
+       return result;
+}
+
+Matrix Matrix::rotation_deg(double a, double x, double y, double z)
+{
+       return rotation(a*M_PI/180, x, y, z);
+}
+
+Matrix Matrix::scaling(double s)
+{
+       Matrix result;
+       result.matrix[0] = s;
+       result.matrix[5] = s;
+       result.matrix[10] = s;
+       result.flags |= SCALE;
+       return result;
+}
+
+Matrix Matrix::scaling(double x, double y, double z)
+{
+       Matrix result;
+       result.matrix[0] = x;
+       result.matrix[5] = y;
+       result.matrix[10] = z;
+       result.flags |= SCALE|ARBITARY;
+       return result;
+}
+
+Matrix Matrix::ortho(double l, double r, double b, double t, double n, double f)
+{
+       if(l==r || b==t || n==f)
+               throw InvalidParameterValue("Orthogonal projection can't have zero dimension");
+
+       Matrix result;
+       result.matrix[0] = 2/(r-l);
+       result.matrix[5] = 2/(t-b);
+       result.matrix[10] = -2/(f-n);
+       result.matrix[12] = -(r+l)/(r-l);
+       result.matrix[13] = -(t+b)/(t-b);
+       result.matrix[14] = -(f+n)/(f-n);
+       result.flags = TRANSLATE|SCALE|ARBITARY;
+       return result;
+}
+
+Matrix Matrix::ortho_centered(double w, double h)
+{
+       return ortho(-w/2, w/2, -h/2, h/2, -1, 1);
+}
+
+Matrix Matrix::ortho_bottomleft(double w, double h)
+{
+       return ortho(0, w, 0, h, -1, 1);
+}
+
+Matrix Matrix::ortho_topleft(double w, double h)
+{
+       return ortho(0, w, h, 0, -1, 1);
+}
+
+Matrix Matrix::frustum(double l, double r, double b, double t, double n, double f)
+{
+       if(l==r || b==t || n<=0 || f<=n)
+               throw InvalidParameterValue("Invalid frustum parameters");
+
+       Matrix result;
+       result.matrix[0] = 2*n/(r-l);
+       result.matrix[5] = 2*n/(t-b);
+       result.matrix[8] = (r+l)/(r-l);
+       result.matrix[9] = (t+b)/(t-b);
+       result.matrix[10] = -(f+n)/(f-n);
+       result.matrix[11] = -1;
+       result.matrix[14] = -2*f*n/(f-n);
+       result.matrix[15] = 0;
+       result.flags = ARBITARY;
+       return result;
+}
+
+Matrix Matrix::frustum_centered(double w, double h, double n, double f)
+{
+       return frustum(-w/2, w/2, -h/2, h/2, n, f);
+}
+
+Matrix Matrix::perspective(double h, double a, double n, double f)
+{
+       double hh = tan(h)*n;
+       return frustum(-hh*a, hh*a, -hh, hh, n, f);
+}
+
+
+GLenum MatrixStack::current_mode = GL_MODELVIEW;
+
+MatrixStack::MatrixStack(GLenum m):
+       mode(m)
+{
+       matrices.push_back(Matrix());
+}
+
+MatrixStack::MatrixStack():
+       mode(0)
+{
+       matrices.push_back(Matrix());
+}
+
+const Matrix &MatrixStack::top()
+{
+       return matrices.back();
+}
+
+void MatrixStack::load(const Matrix &m)
+{
+       matrices.back() = m;
+       update();
+}
+
+void MatrixStack::multiply(const Matrix &m)
+{
+       matrices.back() *= m;
+       update();
+}
+
+void MatrixStack::push()
+{
+       matrices.push_back(top());
+}
+
+void MatrixStack::pop()
+{
+       if(matrices.size()==1)
+               throw InvalidState("Can't pop the last matrix");
+
+       matrices.pop_back();
+       update();
+}
+
+void MatrixStack::update()
+{
+       if(!mode)
+               return;
+
+       if(mode!=current_mode)
+       {
+               glMatrixMode(mode);
+               current_mode = mode;
+       }
+
+       glLoadMatrixd(matrices.back().data());
+}
+
+MatrixStack &MatrixStack::operator=(const Matrix &m)
+{
+       load(m);
+       return *this;
+}
+
+MatrixStack &MatrixStack::operator*=(const Matrix &m)
+{
+       multiply(m);
+       return *this;
+}
+
+MatrixStack &MatrixStack::modelview()
+{
+       static MatrixStack ms(GL_MODELVIEW);
+       return ms;
+}
+
+MatrixStack &MatrixStack::projection()
+{
+       static MatrixStack ms(GL_PROJECTION);
+       return ms;
+}
+
+
+// Deprecated stuff
+
+MatrixStack *active_stack = &MatrixStack::modelview();
+
 void matrix_mode(MatrixMode m)
 {
-       glMatrixMode(m);
+       if(m==MODELVIEW)
+               active_stack = &MatrixStack::modelview();
+       else if(m==PROJECTION)
+               active_stack = &MatrixStack::projection();
+       else
+               throw InvalidParameterValue("Texture matrices are not supported");
 }
 
 void load_identity()
 {
-       glLoadIdentity();
+       *active_stack = Matrix();
 }
 
 void load_matrix(const float *matrix)
 {
-       glLoadMatrixf(matrix);
+       *active_stack = Matrix(matrix);
 }
 
 void load_matrix(const double *matrix)
 {
-       glLoadMatrixd(matrix);
+       *active_stack = Matrix(matrix);
 }
 
 void mult_matrix(const float *matrix)
 {
-       glMultMatrixf(matrix);
+       *active_stack *= Matrix(matrix);
 }
 
 void mult_matrix(const double *matrix)
 {
-       glMultMatrixd(matrix);
+       *active_stack *= Matrix(matrix);
 }
 
 void push_matrix()
 {
-       glPushMatrix();
+       active_stack->push();
 }
 
 void pop_matrix()
 {
-       glPopMatrix();
+       active_stack->pop();
 }
 
 void translate(float x, float y, float z)
 {
-       glTranslatef(x, y, z);
+       *active_stack *= Matrix::translation(x, y, z);
 }
 
 void rotate(float a, float x, float y, float z)
 {
-       glRotatef(a, x, y, z);
+       *active_stack *= Matrix::rotation_deg(a, x, y, z);
 }
 
 void scale(float x, float y, float z)
 {
-       glScalef(x, y, z);
+       *active_stack *= Matrix::scaling(x, y, z);
 }
 
 void scale_uniform(float s)
 {
-       scale(s, s, s);
+       *active_stack *= Matrix::scaling(s);
 }
 
 } // namespace GL
index b574c7359506bf05e9cecc46fd9f4593f721bab2..a2194916e52d80a8e7cd322b1af93e0b3871d223 100644 (file)
@@ -8,11 +8,108 @@ Distributed under the LGPL
 #ifndef MSP_GL_MATRIX_H_
 #define MSP_GL_MATRIX_H_
 
+#include <list>
 #include "gl.h"
 
 namespace Msp {
 namespace GL {
 
+class Matrix
+{
+private:
+       enum Flags
+       {
+               IDENTITY  = 0,
+               TRANSLATE = 1,
+               ROTATE    = 2,
+               SCALE     = 4,
+               ARBITARY  = 8
+       };
+
+       double matrix[16];
+       unsigned flags;
+
+public:
+       Matrix();
+       Matrix(const float *);
+       Matrix(const double *);
+private:
+       void check_flags();
+
+public:
+       const double *data() const { return matrix; }
+
+       void multiply(const Matrix &);
+       void translate(double, double, double);
+       void rotate(double, double, double, double);
+       void rotate_deg(double, double, double, double);
+       void scale(double);
+       void scale(double, double, double);
+
+       Matrix operator*(const Matrix &) const;
+       Matrix &operator*=(const Matrix &);
+       double operator[](unsigned) const;
+
+       static Matrix translation(double, double, double);
+       static Matrix rotation(double, double, double, double);
+       static Matrix rotation_deg(double, double, double, double);
+       static Matrix scaling(double);
+       static Matrix scaling(double, double, double);
+
+       static Matrix ortho(double, double, double, double, double, double);
+       static Matrix ortho_centered(double, double);
+       static Matrix ortho_bottomleft(double, double);
+       static Matrix ortho_topleft(double, double);
+       static Matrix frustum(double, double, double, double, double, double);
+       static Matrix frustum_centered(double, double, double, double);
+       static Matrix perspective(double, double, double, double);
+};
+
+class MatrixStack
+{
+public:
+       class Push
+       {
+       private:
+               MatrixStack &stack;
+
+       public:
+               Push(MatrixStack &s): stack(s) { stack.push(); }
+               ~Push() { stack.pop(); }
+       };
+
+private:
+       GLenum mode;
+       std::list<Matrix> matrices;
+
+       static GLenum current_mode;
+
+       MatrixStack(const MatrixStack &);
+       MatrixStack &operator=(const MatrixStack &);
+       MatrixStack(GLenum);
+public:
+       MatrixStack();
+
+       const Matrix &top();
+       void load(const Matrix &);
+       void multiply(const Matrix &);
+       void push();
+       void pop();
+private:
+       void update();
+
+public:
+       MatrixStack &operator=(const Matrix &);
+       MatrixStack &operator*=(const Matrix &);
+
+       static MatrixStack &modelview();
+       static MatrixStack &projection();
+};
+
+
+/* The stuff below is deprecated and preserved (for now) only for compatibility
+with existing applications */
+
 enum MatrixMode
 {
        MODELVIEW = GL_MODELVIEW,
index 2cc2833f18c23cc597d78b7aed541c1857cabba2..dc690f49f8d65376e7d858f84a0a38488c4b446f 100644 (file)
@@ -6,15 +6,17 @@ Distributed under the LGPL
 */
 
 #include <cmath>
-#include "gl.h"
+#include "matrix.h"
 #include "projection.h"
 
 namespace Msp {
 namespace GL {
 
+extern MatrixStack *active_stack;
+
 void ortho(double left, double right, double bottom, double top, double nearr, double farr)
 {
-       glOrtho(left, right, bottom, top, nearr, farr);
+       *active_stack *= Matrix::ortho(left, right, bottom, top, nearr, farr);
 }
 
 void ortho_centered(double width, double height)
@@ -34,12 +36,12 @@ void ortho_topleft(double width, double height)
 
 void frustum(double left, double right, double bottom, double top, double nearr, double farr)
 {
-       glFrustum(left, right, bottom, top, nearr, farr);
+       *active_stack *= Matrix::frustum(left, right, bottom, top, nearr, farr);
 }
 
 void frustum_centered(double width, double height, double nearr, double farr)
 {
-       glFrustum(-width/2, width/2, -height/2, height/2, nearr, farr);
+       frustum(-width/2, width/2, -height/2, height/2, nearr, farr);
 }
 
 void perspective(double fov_y, double aspect, double nearr, double farr)
index d38aa336cef69458c5bccdbc11cc43e1e72497c6..2be8fd08f883383feb4cbd02bccca838360cf0a2 100644 (file)
@@ -11,6 +11,8 @@ Distributed under the LGPL
 namespace Msp {
 namespace GL {
 
+// Everything in this header is deprecated - use the classes in matrix.h instead
+
 /**
 Sets up an orthogonal projection.
 */