1 module gfm.opengl.matrixstack; 2 3 import gfm.core.memory, 4 gfm.math.vector, 5 gfm.math.matrix; 6 7 /// A matrix stack designed to replace fixed-pipeline matrix stacks. 8 /// This stack always expose both the top element and its inverse. 9 final class MatrixStack(size_t R, T) if (R == 3 || R == 4) 10 { 11 public 12 { 13 alias Matrix!(T, R, R) matrix_t; /// Type of matrices in the stack. Can be 3x3 or 4x4. 14 15 /// Creates a matrix stack. 16 /// The stack is initialized with one element, an identity matrix. 17 this(size_t depth = 32) nothrow 18 { 19 assert(depth > 0); 20 size_t memNeeded = matrix_t.sizeof * depth * 2; 21 void* data = alignedMalloc(memNeeded * 2, 64); 22 _matrices = cast(matrix_t*)data; 23 _invMatrices = cast(matrix_t*)(data + memNeeded); 24 _top = 0; 25 _depth = depth; 26 loadIdentity(); 27 } 28 29 ~this() 30 { 31 close(); 32 } 33 34 /// Releases the matrix stack memory. 35 void close() 36 { 37 if (_matrices !is null) 38 { 39 alignedFree(_matrices); 40 _matrices = null; 41 } 42 } 43 44 /// Replacement for $(D glLoadIdentity). 45 void loadIdentity() pure nothrow 46 { 47 _matrices[_top] = mat4d.identity(); 48 _invMatrices[_top] = mat4d.identity(); 49 } 50 51 /// Replacement for $(D glPushMatrix). 52 void push() pure nothrow 53 { 54 if(_top + 1 >= _depth) 55 assert(false, "Matrix stack is full"); 56 57 _matrices[_top + 1] = _matrices[_top]; 58 _invMatrices[_top + 1] = _invMatrices[_top]; 59 ++_top; 60 } 61 62 /// Replacement for $(D glPopMatrix). 63 void pop() pure nothrow 64 { 65 if (_top <= 0) 66 assert(false, "Matrix stack is empty"); 67 68 --_top; 69 } 70 71 /// Returns: Top matrix. 72 /// Replaces $(D glLoadMatrix). 73 matrix_t top() pure const nothrow 74 { 75 return _matrices[_top]; 76 } 77 78 /// Returns: Inverse of top matrix. 79 matrix_t invTop() pure const nothrow 80 { 81 return _invMatrices[_top]; 82 } 83 84 /// Sets top matrix. 85 /// Replaces $(D glLoadMatrix). 86 void setTop(matrix_t m) pure nothrow 87 { 88 _matrices[_top] = m; 89 _invMatrices[_top] = m.inverse(); 90 } 91 92 /// Replacement for $(D glMultMatrix). 93 void mult(matrix_t m) pure nothrow 94 { 95 mult(m, m.inverse()); 96 } 97 98 /// Replacement for $(D glMultMatrix), with provided inverse. 99 void mult(matrix_t m, matrix_t invM) pure nothrow 100 { 101 _matrices[_top] = _matrices[_top] * m; 102 _invMatrices[_top] = invM *_invMatrices[_top]; 103 } 104 105 /// Replacement for $(D glTranslate). 106 void translate(Vector!(T, R-1) v) pure nothrow 107 { 108 mult(matrix_t.translation(v), matrix_t.translation(-v)); 109 } 110 111 /// Replacement for $(D glScale). 112 void scale(Vector!(T, R-1) v) pure nothrow 113 { 114 mult(matrix_t.scaling(v), matrix_t.scaling(1 / v)); 115 } 116 117 static if (R == 4) 118 { 119 /// Replacement for $(D glRotate). 120 /// Warning: Angle is given in radians, unlike the original API. 121 void rotate(T angle, Vector!(T, 3u) axis) pure nothrow 122 { 123 matrix_t rot = matrix_t.rotation(angle, axis); 124 mult(rot, rot.transposed()); // inversing a rotation matrix is tranposing 125 } 126 127 /// Replacement for $(D gluPerspective). 128 /// Warning: FOV is given in radians, unlike the original API. 129 void perspective(T FOVInRadians, T aspect, T zNear, T zFar) pure nothrow 130 { 131 mult(matrix_t.perspective(FOVInRadians, aspect, zNear, zFar)); 132 } 133 134 /// Replacement for $(D glOrtho). 135 void ortho(T left, T right, T bottom, T top, T near, T far) pure nothrow 136 { 137 mult(matrix_t.orthographic(left, right, bottom, top, near, far)); 138 } 139 140 /// Replacement for $(D gluLookAt). 141 void lookAt(vec3!T eye, vec3!T target, vec3!T up) pure nothrow 142 { 143 mult(matrix_t.lookAt(eye, target, up)); 144 } 145 } 146 } 147 148 private 149 { 150 size_t _top; // index of top matrix 151 size_t _depth; 152 matrix_t* _matrices; 153 matrix_t* _invMatrices; 154 } 155 } 156 157 unittest 158 { 159 auto s = new MatrixStack!(4u, double)(); 160 161 s.loadIdentity(); 162 s.push(); 163 s.pop(); 164 165 s.translate(vec3d(4,5,6)); 166 s.scale(vec3d(0.5)); 167 }