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