# Learning Objectives

Extend your C++ vector class to perform multiplication by a matrix that will scale and translate an object.

# Introduction

Matrix multiplication is the mathematical workhorse used for rendering motion. With it, transformations of all kinds can be performed. These include scaling, translations, rotations, reflections, and shearing. This experiment will lay the foundation to perform all of them, but implement only the first two.

Ideally, since a matrix is not the same kind of object as a vector, an integer, a float, or any other object, we should create (or use) a matrix class to work with matrices. However, since we are limited to only a few hours during a single semester, we shall adopt an approach that will save us significant development time. A matrix can be considered as an array of vectors. Specifically, if we have the equation , where A is a matrix and $\vec{v}$ and $\vec{v}^\prime$ are vectors, then we shall think of the components of these objects in the following manner:

(1)The matrix A is the 4x4 matrix above, and is treated as an array of four row vectors, , where n is the row number (from 0 to 3). Similarly, vector $\vec{v}$ has components (which are scalars, not vectors). The column vector on the right hand side of the equation is $\vec{v}^\prime$ , and its components are the dot products of the rows of A with $\vec{v}$ . Since your vector class should already have a dot product operation defined, it should be fairly easy to add a method which will multiply a vector by a matrix.

However, there are some precautions we need to share. First, the earlier versions of your vector class probably defined the value of the fourth component of each vector to be 1, since that is how we represent 3D locations in a 4D homogeneous system. But the row vectors of matrix A may have a fourth component different than 1. Therefore, you will need to accommodate this situation in your code. Probably, depending on how you wrote your code, you would be able to overload your constructor by adding a method which accepts four floats as input, rather than three.

A second precaution is related to the dot product. Your original implementation of the dot product ignored the fourth components entirely, so that correct results would be obtained for the dot product of two 3D vectors. But by defining matrix multiplication through dot products (as above), we need to have a dot product which will use the fourth component. Overloading will probably not work here, since the inputs for both the 3D and the 4D dot product are (depending on how you wrote your code) probably two vectors. So you probably need another method with a different function name.

Having addressed the issues surrounding 4D dot products and constructors, you should now be able to add a method to your code which multiplies a vector by a matrix. But in game programming, we normally do not multiply by a completely general matrix. Our interest is in multiplying only by those matrices which correspond to physical situations. In this experiment, that means multiplying by a scaling matrix, or by a translation matrix. We can simplify the mathematics and the coding greatly if we concentrate only on those situations, and write two separate methods, one for scaling and one for translations. Pseudocode for this method could have the following form:

• For the given method, declare the input parameters

• Declare any other temporarily needed objects

• Define the row vectors for the matrix, as given by the parameters

• Perform the dot products

• Create the answer vector

• Return the answer vector

Since the raw scaling matrix has some unintended effects, you should also include a scaling operation in your code that would scale about a center. Such an operation would have as input both the scaling parameters and vertices to be scaled. It would then translate the center to the origin, scale, then return the center to its original location.

Although we have spent considerable time discussing the matrix, the object on which the matrix operates is also important. A single vector may represent a single point on the object, but most objects cannot be represented with a single point. A rectangular solid will have eight vertices. Aspects of symmetry may allow you to represent a rectangular solid by fewer than eight vertices, but if your object has the ability to be deformed (into something where faces may no longer be parallel), then all eight vertices will be required to completely represent the object. Therefore, this “simple” solid will require eight 4x1 column vectors, or (more ideally) a 4x8 matrix. Since we have not defined general matrix multiplication in our code, we will treat this solid as an array of eight vectors, and we can perform any matrix operations on the object by looping over the eight different vertices of the object.

# Prelab Questions and Exercises

1. Design your own 3D object, and give the coordinates of its vertices using homogeneous coordinates at a location away from the origin. (To truly be a 3D object, it must have at least four noncollinear points. But if you make your object too complicated, you may not be able to easily interpret the results.)

2. Give the 4x4 matrix that would perform a raw scaling of your object with a 15% increase in length in the x-direction, a 25% decrease in the y-direction, and reflecting it in the z-direction. Then do the multiplication on your object and obtain the new coordinates. (Of course, this scaling will have unintended translation effects.)

3. Determine the center of your object. Translate this center to the origin, then perform the scaling described in question #2, then return the center to the original location. What are the new coordinates?

# Laboratory Procedures

1. Extend your 3D vector class to include the following new methods:

- a general constructor for a 4D vector
- a dot product operation using all four components
- multiplication by a translation matrix
- multiplication by a raw scaling matrix
- multiplication by a scaling about a center matrix

2. Create a C++ program that transforms the object you chose in prelab question #1. Specifically, the program should:

- ask the user for the vertices of the object
- ask the user to enter the type of transformation (translation or raw scaling or scaling about a center)
- ask the user for the needed parameters for that type of transformation
- perform the transformation
- report the new vertices
- repeatedly ask for new transformations until terminated by the user

3. Test your code with the operations described in the prelab questions.

4. Execute the above program, for your instructor’s verification.

initials:

5. Save your class files! We will use them again in the rotation lab.

# Postlab Questions

1. Rounding errors can accumulate, and if not handled well, can cause degradation of an object. Each matrix multiplication of floats could produce some rounding error. A typical float (expressed in scientific notation) will hold roughly seven significant digits. Suppose a space ship has a length of 900 m, and you want the graphics in your program to be accurate to the nearest centimeter. How many significant digits will locations on some points of your ship require? (Explain why.) How many extra significant digits are available in your float? (Why?) If a matrix multiplication causes an error of 1 unit in your last significant digit, what is the minimum number of matrix multiplications that could cause a visible error? (Why?) At 30 frames per second, how quickly could errors become visible? (Why?)

2. One way to avoid object degradation is to avoid doing matrix multiplications on each vertex separately. Instead, the new location of the center of the object in the world system is computed, then the vertices of the object are located based upon the center. Explain why this approach would help avoid object degradation.