From: Mikko Rasa Date: Tue, 2 Nov 2010 13:10:08 +0000 (+0000) Subject: Replace GL matrices with an object-oriented implementation X-Git-Url: http://git.tdb.fi/?a=commitdiff_plain;h=1d59ea8601436164f8bfc114da3941cb0871e87b;p=libs%2Fgl.git Replace GL matrices with an object-oriented implementation --- diff --git a/source/matrix.cpp b/source/matrix.cpp index 9af850d2..d8f018aa 100644 --- a/source/matrix.cpp +++ b/source/matrix.cpp @@ -5,69 +5,413 @@ Copyright © 2007 Mikko Rasa, Mikkosoft Productions Distributed under the LGPL */ +#include +#include +#include #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 diff --git a/source/matrix.h b/source/matrix.h index b574c735..a2194916 100644 --- a/source/matrix.h +++ b/source/matrix.h @@ -8,11 +8,108 @@ Distributed under the LGPL #ifndef MSP_GL_MATRIX_H_ #define MSP_GL_MATRIX_H_ +#include #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 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, diff --git a/source/projection.cpp b/source/projection.cpp index 2cc2833f..dc690f49 100644 --- a/source/projection.cpp +++ b/source/projection.cpp @@ -6,15 +6,17 @@ Distributed under the LGPL */ #include -#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) diff --git a/source/projection.h b/source/projection.h index d38aa336..2be8fd08 100644 --- a/source/projection.h +++ b/source/projection.h @@ -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. */