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