1 module gl3n.ext.matrixstack; 2 3 private { 4 import gl3n.util : is_matrix; 5 } 6 7 8 /// A matrix stack similiar to OpenGLs glPushMatrix/glPopMatrix 9 struct MatrixStack(T) if(is_matrix!T) { 10 alias T Matrix; /// Holds the internal matrix type 11 12 Matrix top = Matrix.identity; /// The top matrix, the one you work with 13 private Matrix[] stack; 14 private size_t _top_pos = 0; 15 16 alias top this; 17 18 /// If the stack is too small to hold more items, 19 /// space for $(B realloc_interval) more elements will be allocated 20 size_t realloc_interval = 8; 21 22 deprecated("Use matrixStack() instead.") 23 @disable this(); 24 25 /// Sets the stacks initial size to $(B depth) elements 26 deprecated("Use matrixStack() instead.") 27 this(size_t depth) pure nothrow { 28 stack = new Matrix[](depth); 29 } 30 31 /// Sets the top matrix 32 void set(Matrix matrix) pure nothrow { 33 top = matrix; 34 } 35 36 /// Pushes the top matrix on the stack and keeps a copy as the new top matrix 37 void push() pure nothrow { 38 if(stack.length <= _top_pos) { 39 stack.length += realloc_interval; 40 } 41 42 stack[_top_pos++] = top; 43 } 44 45 /// Pushes the top matrix on the stack and sets $(B matrix) as the new top matrix. 46 void push(Matrix matrix) pure nothrow { 47 push(); 48 top = matrix; 49 } 50 51 /// Pops a matrix from the stack and sets it as top matrix. 52 /// Also returns a reference to the new top matrix. 53 ref Matrix pop() pure nothrow 54 in { assert(_top_pos >= 1, "popped too often from matrix stack"); } 55 do { 56 top = stack[--_top_pos]; 57 return top; 58 } 59 } 60 61 /// Constructs a new stack with an initial size of $(B depth) elements 62 MatrixStack!T matrixStack(T)(size_t depth = 16) pure nothrow { 63 typeof(return) res = MatrixStack!T.init; 64 res.stack.length = depth; 65 return res; 66 } 67 68 unittest { 69 import gl3n.linalg : mat4; 70 71 static assert(!__traits(compiles, {auto m = MatrixStack!mat4();})); 72 static assert(!__traits(compiles, {MatrixStack!mat4 m;})); 73 auto m1 = matrixStack!mat4(); 74 assert(m1.stack.length == 16); 75 auto m2 = matrixStack!mat4(20); 76 assert(m2.stack.length == 20); 77 78 assert(m1.top == mat4.identity); 79 assert(m1._top_pos == 0); 80 } 81 82 unittest { 83 import gl3n.linalg : mat4; 84 85 auto ms = matrixStack!mat4(); 86 // just a few tests to make sure it forwards correctly to Matrix 87 static assert(__traits(hasMember, ms, "make_identity")); 88 static assert(__traits(hasMember, ms, "transpose")); 89 static assert(__traits(hasMember, ms, "invert")); 90 static assert(__traits(hasMember, ms, "scale")); 91 static assert(__traits(hasMember, ms, "rotate")); 92 93 assert(ms.top == mat4.identity); 94 assert(ms == ms.top); // make sure there is an proper alias this 95 ms.push(); 96 97 auto m1 = mat4(1, 0, 0, 0, 98 0, 0, 0, 0, 99 0, 0, 1, 0, 100 0, 0, 0, 1); 101 102 ms.set(m1); 103 assert(ms.top == m1); 104 assert(ms == ms.top); 105 ms.push(); 106 107 assert(ms.top == m1); 108 ms.top = ms.translate(0, 3, 2); 109 ms.push(mat4.identity); 110 111 assert(ms.top == mat4.identity); 112 ms.push(); 113 114 ms.pop(); 115 assert(ms.top == mat4.identity); 116 117 ms.pop(); 118 assert(ms.top == mat4(m1).translate(0, 3, 2)); 119 120 ms.pop(); 121 assert(ms.top == m1); 122 123 ms.pop(); 124 assert(ms.top == mat4.identity); 125 }