+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;
+}
+
+Vector4 Matrix::operator*(const Vector4 &vec) const
+{
+ if(flags==IDENTITY)
+ return vec;
+ else if(flags==TRANSLATE)
+ return Vector4(vec.x+vec.w*matrix[12], vec.y+vec.w*matrix[13], vec.z+vec.w*matrix[14], vec.w);
+ else if(flags==SCALE)
+ return Vector4(vec.x*matrix[0], vec.y*matrix[5], vec.z*matrix[10], vec.w);
+ else
+ {
+ Vector4 result;
+ result.x = vec.x*matrix[0]+vec.y*matrix[4]+vec.z*matrix[8]+vec.w*matrix[12];
+ result.y = vec.x*matrix[1]+vec.y*matrix[5]+vec.z*matrix[9]+vec.w*matrix[13];
+ result.z = vec.x*matrix[2]+vec.y*matrix[6]+vec.z*matrix[10]+vec.w*matrix[14];
+ result.w = vec.x*matrix[3]+vec.y*matrix[7]+vec.z*matrix[11]+vec.w*matrix[15];
+ return result;
+ }
+}
+
+double Matrix::operator[](unsigned i) const
+{
+ if(i>=16)
+ throw out_of_range("Matrix::operator[]");
+ 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)