1 module gfmod.opengl.matrixstack; 2 3 import gl3n.linalg; 4 5 6 /** A matrix stack designed to replace fixed-pipeline matrix stacks. 7 * 8 * This stack always exposes both the top element and its inverse. 9 */ 10 class MatrixStack(F, size_t depth = 32) 11 if(depth > 0 && (is(F == float) || is(F == double))) 12 { 13 private: 14 size_t _top; // index of top matrix 15 M[depth] _matrices; 16 M[depth] _invMatrices; 17 18 public: 19 /// The matrix type this stack works with. 20 alias M = Matrix!(F, 4, 4); 21 22 /// Creates a matrix stack. 23 /// The stack is initialized with one element, an identity matrix. 24 this() @safe pure nothrow @nogc 25 { 26 _top = 0; 27 loadIdentity(); 28 } 29 30 /// Replacement for $(D glLoadIdentity). 31 void loadIdentity() @safe pure nothrow @nogc 32 { 33 _matrices[_top] = M.identity(); 34 _invMatrices[_top] = M.identity(); 35 } 36 37 /// Replacement for $(D glPushMatrix). 38 void push() @safe pure nothrow @nogc 39 { 40 if(_top + 1 >= depth) { assert(false, "Matrix stack is full"); } 41 42 _matrices[_top + 1] = _matrices[_top]; 43 _invMatrices[_top + 1] = _invMatrices[_top]; 44 ++_top; 45 } 46 47 /// Replacement for $(D glPopMatrix). 48 void pop() @safe pure nothrow @nogc 49 { 50 if (_top <= 0) { assert(false, "Matrix stack is empty"); } 51 52 --_top; 53 } 54 55 /// Returns: Top matrix. 56 /// Replaces $(D glLoadMatrix). 57 M top() @safe pure const nothrow @nogc { return _matrices[_top]; } 58 59 /// Returns: Inverse of top matrix. 60 M invTop() @safe pure const nothrow @nogc { return _invMatrices[_top]; } 61 62 /// Sets top matrix. 63 /// Replaces $(D glLoadMatrix). 64 void setTop(M m) @safe pure nothrow @nogc 65 { 66 _matrices[_top] = m; 67 _invMatrices[_top] = m.inverse(); 68 } 69 70 /// Replacement for $(D glMultMatrix). 71 void mult(M m) @safe pure nothrow @nogc { mult(m, m.inverse()); } 72 73 /// Replacement for $(D glMultMatrix), with provided inverse. 74 void mult(M m, M invM) @safe pure nothrow @nogc 75 { 76 _matrices[_top] = _matrices[_top] * m; 77 _invMatrices[_top] = invM *_invMatrices[_top]; 78 } 79 80 /// Replacement for $(D glTranslate). 81 void translate(Vector!(F, 3) v) @safe pure nothrow @nogc { translate(v.x, v.y, v.z); } 82 83 /// Ditto. 84 void translate(F x, F y, F z) @safe pure nothrow @nogc 85 { 86 mult(M.translation(x, y, z), M.translation(-x, -y, -z)); 87 } 88 89 /// Replacement for $(D glScale). 90 void scale(Vector!(F, 3) v) @safe pure nothrow @nogc { scale(v.x, v.y, v.z); } 91 92 /// Replacement for $(D glScale). 93 void scale(F x, F y, F z) @safe pure nothrow @nogc 94 { 95 mult(M.scaling(x, y, z), M.scaling(1 / x, 1 / y, 1 / z)); 96 } 97 98 99 /// Replacement for $(D glRotate). 100 /// Warning: Angle is given in radians, unlike the original API. 101 void rotate(F angle, Vector!(F, 3) axis) @safe pure nothrow @nogc 102 { 103 M rot = M.rotation(angle, axis); 104 mult(rot, rot.transposed()); // inversing a rotation matrix is tranposing 105 } 106 107 /// Replacement for $(D gluPerspective). 108 void perspective(F left, F right, F bottom, F top, F near, F far) 109 @safe pure nothrow @nogc 110 { 111 mult(M.perspective(left, right, bottom, top, near, far)); 112 } 113 114 /// Replacement for $(D glOrtho). 115 void ortho(F left, F right, F bottom, F top, F near, F far) 116 @safe pure nothrow @nogc 117 { 118 mult(M.orthographic(left, right, bottom, top, near, far)); 119 } 120 } 121 122 unittest 123 { 124 auto s = new MatrixStack!double(); 125 126 s.loadIdentity(); 127 s.push(); 128 s.pop(); 129 130 s.translate(vec3d(4,5,6)); 131 s.scale(vec3d(0.5)); 132 }