CSCI 1300
Introduction to Computer Science
Homework 11: Perspective 3D Display

Due Date

You must submit your program called perspec.cxx by 10pm on Friday, Dec 9. Submit the programming work online to http://culearn.colorado.edu. Late work is accepted until 10am on Monday, Dec 12 with no penalty. Note that no very late work is accepted for this assignment. The final submission time is at exactly 10am on Monday, Dec 12. Please don't leave it until the last minute because computer problems could cause you to not be able to submit.

Working Alone

You may talk with other students and instructors about the assignments, but you may not look at or copy code written by others. The penalty for violating this code is an F for the entire semester.

Programming Style

As always, all your work must follow these ten essential style items. See www.cs.colorado.edu/~main/style.html.

The Assignment

Please make the following modifications to your previous homework:

  1. Change wmax to a global named constant called WMAX with a value of 14000.0. For wmin, you should now use -WMAX.

  2. Modify your main program so that it uses double buffering and has an infinite animation loop with these steps:
    • Clear the screen
    • Draw the wire-frame
    • Swapbuffers
    • and finally call a function named interact, described below.

  3. Write a function named interact that allows the user to change the coordinates of all the points with a single keystroke. You may use my exact interact function if you like:
    //-----------------------------------------------------------------------------
    void interact(double data[M][3])
    {
        while (!kbhit( ))
        {
    	delay(10);
        }
        switch (getch( ))
        {
        case '/': transform(data, CLOCKWISE); break;
        case '\\': transform(data, COUNTERCLOCKWISE); break;
        case KEY_UP: transform(data, UP); break;
        case KEY_DOWN: transform(data, DOWN); break;
        case KEY_LEFT: transform(data, LEFT); break;
        case KEY_RIGHT: transform(data, RIGHT); break;
        case '+': transform(data, ZOOMIN); break;
        case '-': transform(data, ZOOMOUT); break;
        }
    }
    //-----------------------------------------------------------------------------
    
    Notice that the while-loop waits until the user presses a key. Then the switch-statement reads that key (via getch) and uses the result to control how the transform function is called. More information about transform is given below. For now, notice that there are several special keys that the user can press including the four arrow keys and the backslash key (which is written as '\\' in C++).

  4. Here's what the transform function does: The first parameter contains the data for your points. The second parameter is a 3 by 3 matrix of double numbers. I want you to imagine that each of the data points is a 1 by 3 row vector, like this:
    [ x    y    z ]
    Each and every point is transformed by the 3 by 3 matrix. In particular, the new value of each point is obtained by this matrix multiplication, where the 3 by 3 matrix is on the right (I put dots for its entries) and the original value of the point is the row vector on the left:
      [ ]
    ...
    [ x    y    z ] ...
    ...

  5. I'll explain where the matrices come from in class. For now, all you need to know is that they are defined as global named constants:
    // Constants used in the transformation matrices:
    const double ANGLE = M_PI/100;
    const double SIN = sin(ANGLE);
    const double COS = cos(ANGLE);
    const double ZOOM = 1.1;
    
    // The transformation matrices:
    const double ZOOMIN[3][3] = {{ZOOM, 0, 0}, {0, ZOOM, 0}, {0, 0, ZOOM}};
    const double ZOOMOUT[3][3] = {{1/ZOOM, 0, 0}, {0, 1/ZOOM, 0}, {0, 0, 1/ZOOM}};
    const double CLOCKWISE[3][3] = {{COS, -SIN, 0}, {SIN, COS, 0}, {0, 0, 1}};
    const double COUNTERCLOCKWISE[3][3] = {{COS, SIN, 0}, {-SIN, COS, 0}, {0, 0, 1}};
    const double DOWN[3][3] = {{1, 0, 0}, {0, COS, -SIN}, {0, SIN, COS}};
    const double UP[3][3] = {{1, 0, 0}, {0, COS, SIN}, {0, -SIN, COS}};
    const double RIGHT[3][3] = {{COS, 0, SIN}, {0, 1, 0}, {-SIN, 0, COS}};
    const double LEFT[3][3] = {{COS, 0, -SIN}, {0, 1, 0}, {SIN, 0, COS}};
    

  6. One last change: Instead of drawing an orthogonal projection (which ignores the value of z), I'd like you to draw a perspective projection. In this projection, the x-coordinate that you draw depends on a distance called EYE that specifies how far the eyeball is from the origin along the negative z axis. We will use EYE=75000. With this value, you should draw an x-coordinate as if it were at this location instead of x:
    (x*EYE)/(EYE+z)
    
    In this formula, the z is the actual z coordinate of the point that you are drawing. Use the same formula for figuring out where to draw a y-coordinate. I'll explain where the formula comes from in class.

By the way, our approach is close to the way that 3-d graphics programs work, but there are several differences that I'll explain in class.