//****************************************************************************// //***************** Matrix Stack Basics - January 14th, 2019 ***************// //**************************************************************************// - Paper handouts? In this digital age?! MADNESS!!! - On the intro project: ...well, it looks like luckily I did it correctly, so yay? --------------------------------------------------------------- - Alright, first things first, let's talk about OpenGL - "the library that Processing, and many other apps, use to draw things" - You'll be creating your own basic implementation of this (and the matrix stack) for project 1 - so pay attention! - Let's say we want to draw a simple line - how'll we do this? Well, we'll start off doing this: glBeginShape(GL_LINES); // place the endpoints of the line glVertex(100.0, 400.0); glVertex(100.0, 100.0); glEnd(); // more important for filling the shape later on - We can use this same strategy to draw more complicated shapes; for instance, to draw two CONNECTED lines: glBeginShape(GL_LINES); glVertex(100.0, 400.0); glVertex(100.0, 100.0); //if we add more lines, because of the GL_LINES param, it'll draw a DIFFERENT line between the 3rd/4th vertex, 5th/6th, etc., so we need to put another vertex on top of the previous one to connect them glVertex(100.0, 400.0); glVertex(400.0, 400.0); glEnd(); - And here's how we would draw a circle (basically: little lines): void unitCircle() { glBeginShape(GL_LINES); xOld = 1, yOld = 0; int numVertices = 20; for (int i = 1, i <= numVertices, i++) { theta = 2*PI* i / ((float) numVertices); int x = cos(theta), y = sin(theta); gtVertex(x, y); gtVertex(xOld, yOld); xOld = x; yOld = y; } glEnd(); } - "But this unit circle would be 2 pixels across - that's tiny! You can't even see it!" - So, to fix this problem, we'll use something called the matrix stack - The MATRIX STACK, basically, lets us perform different transformations on things and work with them in a translated state - It has a few different components: - The "current transformation matrix" - CTM - represents the current topmost transformation that's applied to what we're drawing - glPushMatrix() copies the CTM and pushes the copy onto the top of the stack - glPopMatrix() will pop off the top of the matrix stack - we just throw the removed matrix away - "You should always have 1 push for each pop - no more, no less. They're like parentheses that way; they have to be balanced." - glTranslate(x, y, z) - translates the component - glTranslate(x, y, z) - scales the component - glRotate(?) - ...for now, just pretend this rotates by some angle - "Each of these 3 commands creates a transformation matrix AND multiplies it on the right-hand side of the CTM" - glVertex(x, y) - position multiplied by the CTM before drawing - e.g. to translate our line combination, we'd say: glPushMatrix(); glTranslate(200, 0); glBeginShape(); (...do our vertex placing...) glEnd(); glPopMatrix(); - "Why didn't we just add to the X and Y coordinates?" In this example, we could've done that - but for more complicated operations, we want the matrix stack to handle all that for us - For instance: void circleImage() { // 1 glPushMatrix(); // 2 glTranslate(0.5, 0.5); // 3 glScale(0.5, 0.5); // 4 circle(); glPopMatrix(); glPushMatrix(); // Now, we're altering our ORIGINAL matrix stack, since popping reset the stack glTranslate(0.5, 0.25); glScale(0.5, 0.5); circle(); glPopMatrix(); } - What does the stack actually look like after first calling "pushMatrix" and Translate/Scale, then? 1) ----------------- CTM |Identity Matrix| => 2) ----------------- CTM |Identity Matrix| ----------------- |Identity Matrix| => 3) ----------------- CTM |I * Translate | ----------------- |Identity Matrix| => 4) ----------------- CTM |I*T * Scale | ----------------- |Identity Matrix| - "So, push says that we're getting ready to transform things; it creates a fresh copy of the current state for us to work with, so that when we pop, the stack goes back to the original state!" - A quick side-note: Professor Turk likes giving matrix stack traces/drawing the matrix stack as exam problems, so be aware! - So, there are 3 big uses for a matrix stack: 1) Changing the coordinate system we're using 2) Instantiation (object re-use, basically) 3) Hierarchy creation (i.e. objects composed of sub-objects) - ...this is what our next example illustrates: - Let's say we want to draw a stick figure with OpenGL, with a torso, two arms, a hand for each arm, and two fingers for each hand - You can view this as a dependency tree, where our overall "stick figure" object is the parent of the torso, the torso is the parent of 2 arms, each of which is the parent of a hand, each of which is the parent of 2 fingers - "The source code for this is given in the handout/on Canvas, so make sure you look at that!" - Here, we have a "square" command that just draws a box centered at the origin - but by using the matrix stack, we can translate, scale, and rotate this square to draw all of our stick figure's components! - And since the matrix stack is persistent, we can then push, say, a rotation on the stack, then call a "drawHand()" method, and all of its drawing methods will be rotated accordingly! - So, we can rotate the whole component thanks to the MS without having to change our original methods! - e.g.: void drawHand() { pushMatrix(); rotate(PI/4.0); scale(1.0, 0.25); translate(1.0, 0.0); drawBox(); popMatrix(); } - Okay, we'll finish this example (and keep pushing on) on Wednesday.