= Solutions to Assignment 2 Questions =
== Question 1 ==
''Describe libST's representation of a transform and compare it's flexibility to an alternative representation, such as the 4x4 matrix representation Pat discussed in class. For example, can you name a type of transformation that STTransform cannot represent. Describe the order that the STSceneNode class performs the various rotate/scale/translate transforms when drawing the scene (ie. what is the first operation performed on the shapes vertices? what is the second? third?). Are there other orderings that would produce the same result?''
libST represents transforms using 10 floating point numbers. These numbers describe the x,y,z components of a translation (tx, ty, tz), the x,y,z components of a scale (sx, sy, sz), and the amount of rotation, in degrees, (theta) about an axis (rotz, roty, rotz). As a result, the libST STTransform class can only describe transforms that are a combination of rotations, scales, and translates. More specifically, it can only represent combinations of a these 3 transforms that are applied in a certain order. Examples of significant classes of transforms that cannot be represented given the current implementation of the STTransform include shears and projections.
One of the subtle, but key concepts in assignment 2 and the transforms lecture is that transforms can be thought of as moving the object (operating on the vertices of the shape), or as moving the coordinate axes. When thinking in terms of moving the coordinate axes, then the {{{STSceneNode::Draw}}} method first applies the translation, then the rotation, and lastly the scaling transform. Note that this order is the order of the glTranslatef, glRotatef, and glScalef calls in the code. Conversely, if you think about transforms in terms of moving the object’s vertices, then the transforms are applied in the reverse order. The vertices are scaled, then rotated, then translated.
The order of the scale and rotation can be interchanged without modifying the effect of the transform. Interchanging either the rotation or the scaling with the translation can produce a different transformation. A significant number of students mentioned that the scaling transform could be reordered with respect to the translation or rotation without changing the effect of the STTransform. Here is an example to illustrate why that is not the case.
Assume you have a vertex (1,0,0) and when to transform it using an STTransform object that contains parameters for a translation of (0,1,0), and a scale of (2,2,1). Applying the scale and then the translate to the vertex gives you (2,1,0). Applying the translate and then the scale to the vertex gives you (2,2,0).
== Question 2 ==
''Note that graphical objects, such as instances of the STImage and STShape class are not scene graph nodes (but they can be contained within STSceneNodes). By inspecting the implementation of STSceneNode, determine if it permissible to add the same object to the scene multiple times (Eg. Can multiple scene nodes contain the same shape object)? Why would one want to do this?''
Yes, the same libST object implementing the STDrawable interface can be inserted into the scene graph many times. This practice is called “instancing”, and is useful when a scene contains the same object repeated over and over. A good example of when instances might be useful is rendering a tree full of leaves. A tree might have thousands of leaves, but the same shape object might be used for each leaf. A different transform is applied to each node containing the leaf shape to place it on the tree. Instancing can make it easier to create complex objects (only have to load and setup the parameters of the object once) and can make rendering complex scenes much more efficient (in terms of processing and memory).
== Question 3 ==
''Assume you had an STSceneNode n, and a STShape s. How would you add this shape as the first child node of n? How would you add it as the last child node of n?''
To insert as first child: {{{n->PlaceObject( s, NULL)}}}
To insert as last child: {{{n->PlaceObject( s, n->GetLastChild() )}}}
Note the above two lines of code are equivalent to:
Insert as first child:
{{{STSceneNode* node = n->CreateNode(NULL)}}}
{{{node->SetObject(s);}}}
Insert as last child:
{{{STSceneNode* node = n->CreateNode(n->GetLastChild());}}}
{{{node->SetObject(s);}}}
== Question 4 ==
''The {{{STImage::read()}}} method doesn't read data off disk, it reads it from the OpenGL framebuffer. The method takes 4 integer arguments. Describe the meaning of these arguments. You will call this method as part of the required code for this assignment. How are the values you pass to STImage::read() determined in clock.cpp?''
The 4 arguments define the region of the OpenGL framebuffer that should be read to create the contents of the STImage object. The first two arguments define the screen coordinates of the start of the region, where (0, 0) is the bottom left corner of the screen. The third and fourth arguments specify the width and height of the region to read. In clock.cpp, the code is specified to read the entire framebuffer. As a result, the starting position is always (0,0) and the width and height of the region correspond to the current size of the screen. The current size of the screen is stored by the application as part of the GLUT resize event handler.