- Preliminary concepts and interfaces
- TheBasics.c++ (Instantiating points and vectors; overloaded arithmetic operators; using some basic methods)
- Some very useful public instance methods in
`class AffVector`void arbitraryNormal(AffVector& normal) const; // to "this" AffVector cross(const AffVector& rhs) const; // return this x rhs void decompose(const AffVector& arbitraryVector, // with respect to "this" AffVector& parallel, AffVector& perpendicular) const; double dot(const AffVector& rhs) const; // return this . rhs double length() const; // of "this" double lengthSquared() const; // of "this" // In the following two "

*normalize*" methods, the return value is the length // of "this" vector before normalization. For example, for |v| > eps: // double L = v.normalize(); // is equivalent to: // double L = v.length(); // v = v / L; double normalize(); // "this" (Unlike all the other methods in 1.b, "this" gets modified) double normalizeToCopy(AffVector& normalizedCopy) const; - Some very useful public class methods in
`class AffVector`:In the first two:

**U**(or**V**) ← component of**U**(or**V**) perpendicular to**W**;*if*the resulting**U**(or**V**) is the zero vector*then***U**(or**V**) ← arbitrary normal to**W****V**←**W**x**U**(or**U**←**V**x**W**)- All three are normalized and returned

void coordinateSystemFromUW(AffVector& U, AffVector& V, AffVector& W); void coordinateSystemFromVW(AffVector& U, AffVector& V, AffVector& W); AffVector cross(const AffVector& v1, const AffVector& v2); double dot(const AffVector& v1, const AffVector& v2);

- Public class variables in
`class AffVector`(these can be used, but not modified):const AffVector xu; // (1, 0, 0) - in your code, you reference this as: "cryph::AffVector::xu" const AffVector yu; // (0, 1, 0) const AffVector zu; // (0, 0, 1) const AffVector zeroVector;

- Some very useful public instance methods in
`class AffPoint`double distanceSquaredTo(const AffPoint& P) const; // distance squared from "this" to "P" double distanceTo(const AffPoint& P) const; // distance from "this" to "P" void toCylindrical(double& r, double& theta, double& z) const; void toSpherical(double& rho, double& theta, double& phi) const;

- Some very useful public class methods in
`class AffPoint`:AffPoint fromCylindrical(double r, double theta, double z); AffPoint fromSpherical(double rho, double theta, double phi);

- Public class variables in
`class AffPoint`(these can be used, but not modified):const AffPoint origin; // e.g., in your code, you reference this as: "cryph::AffPoint::origin" const AffPoint xAxisPoint; // (1, 0, 0) const AffPoint yAxisPoint; // (0, 1, 0) const AffPoint zAxisPoint; // (0, 0, 1)

- Intermediate complexity: Rendering and Querying a Parabola

- Designing 3D geometry "in place"
- pwlApproxCircle.c++: PWL approximation of an arbitrary circle in space
- pwlApproxCylinder.c++: Extending the circle example to create a PWL approximation of an arbitrary cylinder in space
**Exercise**: Create a model consisting of a series of spokes like those on a bicycle wheel, ferris wheel, etc.**Exercise**: Generalize`Block`from`SampleProgramSet3/MandM/Block.c++`:

Suppose the constructor is:`Block(cryph::AffPoint C, cryph::AffVector edge1, cryph::AffVector edge2, cryph::AffVector edge3)`. The bulk of the class remains unchanged. The only changes required are:- How the VBO containing the 8 vertices is generated in
`Block::defineBlock` - How the MC bounding box limits are determined (This can be done in
`Block::defineBlock`as well.) - How the normal vectors are determined. (Also best computed in
`Block::defineBlock`and stored in instance variables.)

- How the VBO containing the 8 vertices is generated in

- Transformation matrices: construction and use
Transformation matrices are used to define view transformations for OpenGL (e.g., MC→EC; EC→LDS), but they can also be used to transform model geometry. We have already seen the

`cryph::Matrix4x4`class methods for creating view transformation matrices. Here we focus on the use of other methods to facilitate geometry modeling.For example, instead of creating our general

`Block`as in 2.d, we may define it in canonical position and orientation (e.g., almost exactly as we first saw it in`MandM`), then create translation and/or rotation and/or scale transformations to place it where we want it in our MC space. Doing so typically requires that we transform both points and (normal) vectors. We will generally create 4x4 matrices for these transformations. Be sure you carefully declare normal vectors as`cryph::AffVector`and points as`cryph::AffPoint`because the overloaded matrix operators behave differently (as they must) when applied to points versus vectors. See, for example, 3.a.- Do not mistakenly define a vector as a point and then transform it:

cryph::Matrix4x4 M = …; cryph::AffPoint myVector = …; … M * myVector …; // WRONG!

Instead, be sure you do:

cryph::Matrix4x4 M = …; cryph::AffVector myVector = …; … M * myVector …; // OK now!

- Suppose a 4x4 Matrix, M, is used to transform geometry, and suppose M
_{3x3}is the upper left 3x3 sub-matrix of M. Recall that normal vectors (i.e., vectors defined as being perpendicular to some surface in your model) must be transformed using (M_{3x3}^{-1})^{T}. Assuming that you only use rotation, translation, and scale, you can simply use M_{3x3}to transform normal vectors. Recall that your shader program will normalize them before doing lighting model computations. If you use shear modeling transformations, then you will need to compute and use (M_{3x3}^{-1})^{T}to transform normal vectors. - Primary modeling transformation interfaces
Like the view transformation matrices we saw earlier (

`lookAt`,`perspective`, et al.), all of the following areprototypes in*class method*`class Matrix4x4`. They are all "factory methods" that return matrices built as specified.`Matrix4x4 xRotationDegrees(double angle);``Matrix4x4 yRotationDegrees(double angle);``Matrix4x4 zRotationDegrees(double angle);``Matrix4x4 xRotationRadians(double angle);``Matrix4x4 yRotationRadians(double angle);``Matrix4x4 zRotationRadians(double angle);``Matrix4x4 generalRotationDegrees(const AffPoint& B, const AffVector& axis, double angle);``Matrix4x4 generalRotationRadians(const AffPoint& B, const AffVector& axis, double angle);``Matrix4x4 mirror(const AffPoint& B, const AffVector& n);``Matrix4x4 translation(const AffVector& trans);``Matrix4x4 translation(double dx, double dy, double dz);``Matrix4x4 scale(double sx, double sy, double sz);`

**Exercise**: Add these declarations and code stubs to`class Block`and then complete the implementations as indicated: Block_canonical.h; Block_canonical.c++.**Exercise**: Show how to create an instance of this Block with sizes 2x3x5, rotated 45 degrees about the*y*-axis, then 45 degrees about the*z*-axis and placed with its corner at (10, -3, 40).

- Do not mistakenly define a vector as a point and then transform it:
- Interfacing with OpenGL
- If you have created a single point or an individual vector and you need
to send it to OpenGL, that is very
straightforward as we saw above. For example:
`glVertexAttrib3f(pvaLoc_mcNormal, n1.dx, n1.dy, n1.dz);` - Oftentimes we generate arrays of points and/or arrays of vectors that we want to
send to VBOs. To facilitate that, use the various
`cryph::AffPoint::aCoords`or`cryph::AffVector::vComponents`methods which allow you to directly copy into`vec3`or simple`float`arrays. For example, sending as a simple`float`array:

void sendToGPU_AsFloatArray(cryph::AffPoint* points, int numPoints) { float* ptsAsFloat = new float[3*numPoints]; for (int i=0 ; i<numPoints ; i++) points[i].aCoords(ptsAsFloat, 3*i); // NOTE: "3*i" when passing as float* int numBytes = 3 * numPoints * sizeof(float); glBufferData(GL_ARRAY_BUFFER, numBytes, ptsAsFloat, GL_STATIC_DRAW); delete [] ptsAsFloat; }

Equivalently, you can send as a

`vec3`array:void sendToGPU_AsVec3Array(cryph::AffPoint* points, int numPoints) { typedef float vec3[3]; vec3* ptsAsVec3 = new vec3[numPoints]; for (int i=0 ; i<numPoints ; i++) points[i].aCoords(ptsAsVec3, i); // NOTE: "i" when passing as vec3* int numBytes = numPoints * sizeof(vec3); glBufferData(GL_ARRAY_BUFFER, numBytes, ptsAsVec3, GL_STATIC_DRAW); delete [] ptsAsVec3; }

Regardless of how you send them, don't forget you also need to establish

`glVertexAttribPointer`and`glEnableVertexAttribArray`properly. - When passing matrices to OpenGL as
`uniform`variables, OpenGL needs to know whether we are passing them in row-major or column-major order. It expects column-major, so the third parameter to the`glUniformMatrix`***family of routines is called "`transpose`" and is set to`false`if you are supplying the matrix in column-major order. The`cryph::Matrix4x4`class has instance methods`extractColMajor`and`extractRowMajor`. Depending on which you use, you will set the "`transpose`" parameter accordingly. For example:cryph::Matrix4x4 mc_ec, ec_lds; ModelView::getMatrices(mc_ec, ec_lds); float m[16]; glUniformMatrix4fv(shaderIF->ppuLoc("mc_ec"), 1, false, mc_ec.extractColMajor(m)); glUniformMatrix4fv(shaderIF->ppuLoc("ec_lds"), 1, false, ec_lds.extractColMajor(m));

- If you have created a single point or an individual vector and you need
to send it to OpenGL, that is very
straightforward as we saw above. For example: