//****************************************************************************//
//***************** 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.