Jason McKesson avatar Jason McKesson committed 85aa188

Added object poles to all of the appropriate tutorials.
This required changes in how the mouse pole's input works.
Also added some images.

Comments (0)

Files changed (22)

Documents/Basics/GenMemoryDiagram.lua

+require "SvgWriter"
+require "vmath"
+require "Viewport"
+require "SubImage"
+require "GridAxis"
+require "_utils"
+
+local subImages = SubImage.SubImage(1, 3, 700, 60, 0, 0);
+
+local coordSize = 10;
+
+local vp = Viewport.Viewport(subImages:SubSize(), {coordSize/2, 0}, coordSize)
+local trans2 = Viewport.Transform2D()
+vp:SetTransform(trans2);
+
+local styleLib = SvgWriter.StyleLibrary();
+
+
+
+local vectorColor = "black";
+local vectorSumColor = "red";
+local pointSize = 15;
+
+styleLib:AddStyle(nil, "label", SvgWriter.Style{
+	stroke="none", fill=vectorColor, font_weight="bold", font_size="15pt",
+	font_family="serif",
+});
+
+styleLib:AddStyle(nil, "test", SvgWriter.Style{ stroke="black"});
+
+styleLib:AddStyle(nil, "bytes", SvgWriter.Style{ stroke="black", stroke_width="1px", fill="lightblue"});
+styleLib:AddStyle(nil, "floats", SvgWriter.Style{ stroke="red", stroke_width="2px", fill="none"});
+styleLib:AddStyle(nil, "vecs", SvgWriter.Style{ stroke="darkblue", stroke_width="3px", fill="none"});
+
+styleLib:AddStyle(nil, "positions", SvgWriter.Style{
+	stroke="none", fill="yellow", fill_opacity="0.3"
+});
+
+styleLib:AddStyle(nil, "colors", SvgWriter.Style{
+	stroke="none", fill="gray", fill_opacity="0.3"
+});
+
+local testLine =
+{
+	vmath.vec2(0, 0),
+	vmath.vec2(coordSize, 0),
+};
+
+testLine = vp:Transform(testLine);
+
+--Block drawing.
+local blockHeight = 0.40;
+
+local function GetBlockArray(numBlocks, leftPos, width)
+	local blockArray = {};
+	
+	blockWidth = width / numBlocks;
+	
+	for i = 1, numBlocks do
+		blockArray[#blockArray + 1] = vmath.vec2(leftPos + (blockWidth * (i - 1)), blockHeight / 2);
+		blockArray[#blockArray + 1] = vmath.vec2(leftPos + (blockWidth * i), -blockHeight / 2);
+	end
+	
+	return blockArray;
+end
+
+local function GetRectSequence(bytesPerValue, valuesPerVec, vecsPerArray, numArrays, leftPos, width)
+	local byteArray = nil;
+	if(bytesPerValue) then
+		local numBytes = bytesPerValue;
+		numBytes = numBytes * (valuesPerVec or 1);
+		numBytes = numBytes * (vecsPerArray or 1);
+		numBytes = numBytes * (numArrays or 1);
+		
+		byteArray = GetBlockArray(numBytes, leftPos, width);
+	end
+	
+	local valueArray = nil;
+	if(valuesPerVec) then
+		local numValues = valuesPerVec;
+		numValues = numValues * (vecsPerArray or 1);
+		numValues = numValues * (numArrays or 1);
+		
+		valueArray = GetBlockArray(numValues, leftPos, width)
+	else
+		valueArray = GetBlockArray(1, leftPos, width)
+	end
+	
+	local vecArray = nil;
+	
+	if(vecsPerArray) then
+		local numVecs = vecsPerArray;
+		numVecs = numVecs * (numArrays or 1);
+		
+		vecArray = GetBlockArray(numVecs, leftPos, width)
+	else
+		if(valuesPerVec) then
+			vecArray = GetBlockArray(1, leftPos, width)
+		end
+	end
+	
+	local arraysArray = nil;
+	if(numArrays) then
+		arraysArray = GetBlockArray(numArrays, leftPos, width);
+	end
+	
+	return byteArray, valueArray, vecArray, arraysArray;
+end
+
+local sequences =
+{
+	{3, 4},
+	{4, 4, 4},
+	{7.4, nil, 4, 3, 2},
+}
+
+local startPos = 2.5;
+
+local rectSequences = {};
+
+for i, sequence in ipairs(sequences) do
+	rectSequences[i] =
+		{GetRectSequence(sequence[2], sequence[3], sequence[4], sequence[5], startPos, sequence[1])};
+end
+
+local rectStyles =
+{
+	"bytes",
+	"floats",
+	"vecs",
+	"arrays",
+}
+
+
+--Text
+local globalLabelOffset = vmath.vec2(0.0, -0.05)
+local labels =
+{
+	{"float (4 bytes):", "g_float"},
+	{"vec4 (16 bytes):", "g_vec4"},
+	{"vertexData:", "g_vertexData"},
+};
+
+local writer = SvgWriter.SvgWriter(ConstructSVGName(arg[0]), {subImages:Size().x .."px", subImages:Size().y .. "px"});
+	writer:StyleLibrary(styleLib);
+	writer:BeginDefinitions();
+		writer:BeginGroup(nil, "g_test");
+			writer:Line(testLine[1], testLine[2], "test");
+		writer:EndGroup();
+		for i=1, #sequences do
+			writer:BeginGroup(nil, labels[i][2]);
+				for j=1, 4 do
+					if(rectSequences[i][j]) then
+						local floatRects = vp:Transform(rectSequences[i][j])
+						for k=1, #floatRects, 2 do
+							local style = rectStyles[j];
+							if(j == 4) then
+								if(k == 1) then style = "positions" else style = "colors" end
+							end
+							writer:Rect2Pt(floatRects[k], floatRects[k + 1], nil, style);
+						end
+					end
+				end
+			writer:EndGroup();
+		end
+	writer:EndDefinitions();
+	
+	for i=1, #labels do
+		local loc = vmath.vec2(0, 0);
+		loc = loc + globalLabelOffset;
+		if(labels[i][3]) then loc = loc + labels[i][3] end
+		loc = subImages:Transform({1, i}, vp:Transform(loc));
+		writer:Text(labels[i][1], loc, "label");
+	end
+
+	
+	for i=1, #labels do
+		writer:Use(labels[i][2], subImages:Offset(1, i), subImages:SubSize());
+	end
+	
+--	writer:Use("g_test", subImages:Offset(1, 3), subImages:SubSize());
+
+writer:Close();
Add a comment to this file

Documents/Basics/MemoryDiagram.svg

Added
New image
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" height="180px" width="700px" >
+	<style type="text/css" ><![CDATA[.floats
+{
+	stroke: red;
+	stroke-width: 2px;
+	fill: none;
+}
+
+.label
+{
+	font-weight: bold;
+	font-size: 15pt;
+	stroke: none;
+	font-family: serif;
+	fill: black;
+}
+
+.bytes
+{
+	stroke: black;
+	stroke-width: 1px;
+	fill: lightblue;
+}
+
+.colors
+{
+	stroke: none;
+	fill: gray;
+	fill-opacity: 0.3;
+}
+
+.positions
+{
+	stroke: none;
+	fill: yellow;
+	fill-opacity: 0.3;
+}
+
+.vecs
+{
+	stroke: darkblue;
+	stroke-width: 3px;
+	fill: none;
+}
+
+.test
+{
+	stroke: black;
+}]]></style>
+	<defs >
+		<g id="g_test" >
+			<line x2="700" y2="30" y1="30" x1="0" class="test" />
+		</g>
+		<g id="g_float" >
+			<rect y="16" x="175" height="28" width="52.5" class="bytes" />
+			<rect y="16" x="227.5" height="28" width="52.5" class="bytes" />
+			<rect y="16" x="280" height="28" width="52.5" class="bytes" />
+			<rect y="16" x="332.5" height="28" width="52.5" class="bytes" />
+			<rect y="16" x="175" height="28" width="210" class="floats" />
+		</g>
+		<g id="g_vec4" >
+			<rect y="16" x="175" height="28" width="17.5" class="bytes" />
+			<rect y="16" x="192.5" height="28" width="17.5" class="bytes" />
+			<rect y="16" x="210" height="28" width="17.5" class="bytes" />
+			<rect y="16" x="227.5" height="28" width="17.5" class="bytes" />
+			<rect y="16" x="245" height="28" width="17.5" class="bytes" />
+			<rect y="16" x="262.5" height="28" width="17.5" class="bytes" />
+			<rect y="16" x="280" height="28" width="17.5" class="bytes" />
+			<rect y="16" x="297.5" height="28" width="17.5" class="bytes" />
+			<rect y="16" x="315" height="28" width="17.5" class="bytes" />
+			<rect y="16" x="332.5" height="28" width="17.5" class="bytes" />
+			<rect y="16" x="350" height="28" width="17.5" class="bytes" />
+			<rect y="16" x="367.5" height="28" width="17.5" class="bytes" />
+			<rect y="16" x="385" height="28" width="17.5" class="bytes" />
+			<rect y="16" x="402.5" height="28" width="17.5" class="bytes" />
+			<rect y="16" x="420" height="28" width="17.5" class="bytes" />
+			<rect y="16" x="437.5" height="28" width="17.5" class="bytes" />
+			<rect y="16" x="175" height="28" width="70" class="floats" />
+			<rect y="16" x="245" height="28" width="70" class="floats" />
+			<rect y="16" x="315" height="28" width="70" class="floats" />
+			<rect y="16" x="385" height="28" width="70" class="floats" />
+			<rect y="16" x="175" height="28" width="280" class="vecs" />
+		</g>
+		<g id="g_vertexData" >
+			<rect y="16" x="175" height="28" width="21.583333333333" class="floats" />
+			<rect y="16" x="196.58333333333" height="28" width="21.583333333333" class="floats" />
+			<rect y="16" x="218.16666666667" height="28" width="21.583333333333" class="floats" />
+			<rect y="16" x="239.75" height="28" width="21.583333333333" class="floats" />
+			<rect y="16" x="261.33333333333" height="28" width="21.583333333333" class="floats" />
+			<rect y="16" x="282.91666666667" height="28" width="21.583333333333" class="floats" />
+			<rect y="16" x="304.5" height="28" width="21.583333333333" class="floats" />
+			<rect y="16" x="326.08333333333" height="28" width="21.583333333333" class="floats" />
+			<rect y="16" x="347.66666666667" height="28" width="21.583333333333" class="floats" />
+			<rect y="16" x="369.25" height="28" width="21.583333333333" class="floats" />
+			<rect y="16" x="390.83333333333" height="28" width="21.583333333333" class="floats" />
+			<rect y="16" x="412.41666666667" height="28" width="21.583333333333" class="floats" />
+			<rect y="16" x="434" height="28" width="21.583333333333" class="floats" />
+			<rect y="16" x="455.58333333333" height="28" width="21.583333333333" class="floats" />
+			<rect y="16" x="477.16666666667" height="28" width="21.583333333333" class="floats" />
+			<rect y="16" x="498.75" height="28" width="21.583333333333" class="floats" />
+			<rect y="16" x="520.33333333333" height="28" width="21.583333333333" class="floats" />
+			<rect y="16" x="541.91666666667" height="28" width="21.583333333333" class="floats" />
+			<rect y="16" x="563.5" height="28" width="21.583333333333" class="floats" />
+			<rect y="16" x="585.08333333333" height="28" width="21.583333333333" class="floats" />
+			<rect y="16" x="606.66666666667" height="28" width="21.583333333333" class="floats" />
+			<rect y="16" x="628.25" height="28" width="21.583333333333" class="floats" />
+			<rect y="16" x="649.83333333333" height="28" width="21.583333333333" class="floats" />
+			<rect y="16" x="671.41666666667" height="28" width="21.583333333333" class="floats" />
+			<rect y="16" x="175" height="28" width="86.333333333333" class="vecs" />
+			<rect y="16" x="261.33333333333" height="28" width="86.333333333333" class="vecs" />
+			<rect y="16" x="347.66666666667" height="28" width="86.333333333333" class="vecs" />
+			<rect y="16" x="434" height="28" width="86.333333333333" class="vecs" />
+			<rect y="16" x="520.33333333333" height="28" width="86.333333333333" class="vecs" />
+			<rect y="16" x="606.66666666667" height="28" width="86.333333333333" class="vecs" />
+			<rect y="16" x="175" height="28" width="259" class="positions" />
+			<rect y="16" x="434" height="28" width="259" class="colors" />
+		</g>
+	</defs>
+	<text y="33.5" x="0" class="label" >float (4 bytes):</text>
+	<text y="93.5" x="0" class="label" >vec4 (16 bytes):</text>
+	<text y="153.5" x="0" class="label" >vertexData:</text>
+	<use xlink:href="#g_float" y="0" x="0" height="60" width="700" />
+	<use xlink:href="#g_vec4" y="60" x="0" height="60" width="700" />
+	<use xlink:href="#g_vertexData" y="120" x="0" height="60" width="700" />
+</svg>

Documents/Basics/Tutorial 02.xml

      0.0f,    0.0f, 1.0f, 1.0f,
 };]]></programlisting>
             </example>
+            <para>First, we need to understand what arrays of data look like at the lowest level. A
+                single byte is the smallest addressible data in C/C++. A byte represents 8 bits (a
+                bit can be 0 or 1), and it is a number on the range [0, 255]. A value of type
+                    <type>float</type> requires 4 bytes worth of storage. Any <type>float</type>
+                value is stored in 4 consecutive bytes of memory.</para>
+            <para>A sequence of 4 floats, in GLSL parlance a <type>vec4</type>, is exactly that: a
+                sequence of four floating-point values. Therefore, a <literal>vec4</literal> takes
+                up 16 bytes, 4 <type>float</type>s times the size of a <type>float</type>.</para>
+            <para>The <varname>vertexData</varname> variable is one large array of floats. The way
+                we want to use it however is as two arrays. Each 4 floats is a single
+                    <type>vec4</type>, and the first three <type>vec4</type>s represents the
+                positions. The next 3 are the colors for the corresponding vertices.</para>
+            <para>In memory, the <varname>vertexData</varname> array looks like this:</para>
+            <figure>
+                <title>Vertex Array Memory Map</title>
+                <mediaobject>
+                    <imageobject>
+                        <imagedata fileref="MemoryDiagram.svg" format="SVG"/>
+                    </imageobject>
+                </mediaobject>
+            </figure>
+            <para>The top two show the layout of the basic data types, and each box is a byte. The
+                bottom diagram shows the layout of the entire array, and each box is a
+                    <type>float</type>. The left half of the box represents the positions and the
+                right half represents the colors.</para>
             <para>The first 3 sets of values are the three positions of the triangle, and the next 3
                 sets of values are the three colors at these vertices. What we really have is two
                 arrays that just happen to be adjacent to one another in memory. One starts at the
                 length) centered at the origin.</para>
             <para>Recall from above that we are sending two pieces of data per-vertex: a position
                 and a color. We have two arrays, one for each piece of data. They may happen to be
-                adjacent to one another, but this changes nothing; there are two arrays of data. We
-                need to tell OpenGL how to get each of these pieces of data.</para>
+                adjacent to one another in memory, but this changes nothing; there are two arrays of
+                data. We need to tell OpenGL how to get each of these pieces of data.</para>
             <para>This is done as follows:</para>
             <example>
                 <title>Rendering the Scene</title>
             </example>
             <para>Since we have two pieces of data, we have two vertex attributes. For each
                 attribute, we must call <function>glEnableVertexAttribArray</function> to enable
-                that particular attribute. The parameter is the attribute location set by the
+                that particular attribute. The first parameter is the attribute location set by the
                     <literal>layout(location)</literal> field for that attribute in the vertex
                 shader.</para>
             <para>Then, we call <function>glVertexAttribPointer</function> for each of the attribute
                 arrays we want to use. The only difference in the two calls are which attribute
-                location to attach this to and the last parameter. The last parameter, if you
-                recall, is the byte offset into the buffer of where the data for this attribute
-                starts. This offset, in this case, is 4 (the size of a float) * 12 (the number of
-                floats in the position data).</para>
+                location to send the data to and the last parameter. The last parameter is the byte
+                offset into the buffer of where the data for this attribute starts. This offset, in
+                this case, is 4 (the size of a <type>float</type>) * 4 (the number of
+                    <type>float</type>s in a <type>vec4</type> * 3 (the number of
+                <type>vec4</type>'s in the position data).</para>
             <note>
-                <para>If you're wondering why it is <literal>(void*)48</literal> and not just 48,
-                    that is because of some legacy API cruft. The reason why the function name is
-                        glVertexAttrib<quote>Pointer</quote> is because the last parameter is
-                    technically a pointer to client memory. Or at least, it could be in the past. So
-                    we must explicitly cast the integer value 48 to a pointer type.</para>
+                <para>If you're wondering why it is <literal>(void*)48</literal> and not just
+                        <literal>48</literal>, that is because of some legacy API cruft. The reason
+                    why the function name is glVertexAttrib<quote>Pointer</quote> is because the
+                    last parameter is technically a pointer to client memory. Or at least, it could
+                    be in the past. So we must explicitly cast the integer value 48 to a pointer
+                    type.</para>
             </note>
             <para>After this, we use <function>glDrawArrays</function> to render, then disable the
                 arrays with <function>glDisableVertexAttribArray.</function></para>
                     last parameter of <function>glVertexAttribPointer</function>.</para>
                 <para>The same goes for <varname>colorAttribArray</varname>, except for the offset
                     value, which is 48 bytes.</para>
-                <para>This is a diagram of the memory for this buffer.</para>
-                <!--TODO: Show a diagram of this memory structure. Show how the float value takes up 4 bytes, then
-show that each 4 floats is a vec4, then show how each vec4 is a single attribute with no space between.
-Also show the offset to the beginning of the color data.-->
                 <para>Using the above pseudo-code representation of the vertex array data,
                         <function>glDrawArrays</function> would be implemented as follows:</para>
                 <example>
                     now:</para>
                 <!--TODO: show two data chunks from the buffer becoming a single set of attribute data. Then show these
 data sets going to vertex shader invocations. Show at least 3 vertex shaders.-->
+                <para>As before, every 3 vertices process is transformed into a triangle.</para>
             </section>
         </section>
         <section>
         <para>In this tutorial, you have learned the following:</para>
         <itemizedlist>
             <listitem>
+                <para>Data is passed to vertex shaders via buffer objects and attribute arrays. This
+                    data is processed into triangles.</para>
+            </listitem>
+            <listitem>
                 <para>The <varname>gl_FragCoord</varname> built-in GLSL variable can be used in
                     fragment shaders to get the window-space coordinates of the current
                     fragment.</para>

Documents/Illumination/Tutorial 08.xml

         <para>This is made more complicated by the fact that light itself is not one thing. There is
             no such thing as <quote>white light.</quote> Virtually all light is made up of a number
             of different wavelengths. Each wavelength (in the visible spectrum) represents a color.
-            The rainbow effect of light scattering, easily seen in a prism, breaks white light into
-            its constituents.</para>
-        <!--TODO: Show a prism defraction of light.-->
-        <note>
-            <para>Developing a lighting model that can actually diffract white light into this
-                pattern is very, <emphasis>very</emphasis> difficult. We won't even come close to
-                such a thing in this book.</para>
-        </note>
-        <para>Colored light simply has fewer wavelengths in it than pure white light. Surfaces
-            interact with light of different wavelengths in different ways. As a simplification of
-            this complex interaction, a surface can do one of two things: absorb that wavelength of
-            light or reflect it.</para>
+            White light is made of many wavelengths (colors) of light. Colored light simply has
+            fewer wavelengths in it than pure white light.</para>
+        <para>Surfaces interact with light of different wavelengths in different ways. As a
+            simplification of this complex interaction, we will assume that a surface can do one of
+            two things: absorb that wavelength of light or reflect it.</para>
         <para>A surface looks blue under white light because the surface absorbs all non-blue parts
             of the light and only reflects the blue parts. If one were to shine a red light on the
             surface, the surface would appear very dark, as the surface absorbs non-blue light, and

Documents/Positioning/GenThreeTriangleOverlap.lua

+require "SvgWriter"
+require "vmath"
+require "Viewport"
+require "SubImage"
+require "GridAxis"
+require "_utils"
+
+local subImages = SubImage.SubImage(1, 1, 250, 250, 0, 0);
+
+local coordSize = 20;
+
+local vp = Viewport.Viewport(subImages:SubSize(), {0, 0}, coordSize)
+local trans2 = Viewport.Transform2D()
+vp:SetTransform(trans2);
+
+local styleLib = SvgWriter.StyleLibrary();
+
+
+local triangleStyles =
+{
+	{"first_tri", "skyblue"},
+	{"second_tri", "orange"},
+	{"third_tri", "yellowgreen"},
+}
+
+local pointSize = 15;
+
+styleLib
+	:AddTable(nil, "base_triangle", {stroke="black", stroke_width="1px"})
+	:AddTable(nil, triangleStyles[1][1], {fill=triangleStyles[1][2]})
+	:AddTable(nil, triangleStyles[2][1], {fill=triangleStyles[2][2]})
+	:AddTable(nil, triangleStyles[3][1], {fill=triangleStyles[3][2]})
+	:AddTable(nil, "overlap_clip", {clip_path=SvgWriter.uriLocalElement("overlap")})
+	
+--Triangles
+
+local trianglePos =
+{
+	{4, 8}, {-5, -8.5}, {-7.5, -6},
+	{-9, -4}, {8.5, -3}, {7.5, -6.5},
+	{6, -9}, {1, 9}, {-2, 7},
+}
+
+local outTris = {};
+for i, position in ipairs(trianglePos) do
+	outTris[i] = vp:Transform(vmath.vec2(unpack(position)));
+end
+
+local writer = SvgWriter.SvgWriter(ConstructSVGName(arg[0]), {subImages:Size().x .."px", subImages:Size().y .. "px"});
+	writer:StyleLibrary(styleLib);
+	writer:BeginDefinitions();
+		writer:BeginClipPath(nil, "overlap");
+			local points = vp:Transform{vmath.vec2(-coordSize/2, coordSize/2),
+				vmath.vec2(coordSize/2, 0)}
+			writer:Rect2Pt(points[1], points[2], nil, {"show_clip"});
+		writer:EndClipPath();
+	writer:EndDefinitions();
+
+	for i=1, #outTris, 3 do
+		writer:Polygon({outTris[i], outTris[i+1], outTris[i+2]},
+			{"base_triangle", triangleStyles[math.ceil(i/3)][1]});
+	end
+
+	--Draw the overlapping region.
+	writer:Polygon({outTris[1], outTris[2], outTris[3]},
+		{"base_triangle", triangleStyles[1][1], "overlap_clip"});
+
+writer:Close();
Add a comment to this file

Documents/Positioning/ThreeTriangleOverlap.svg

Added
New image
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" height="250px" width="250px" >
+	<style type="text/css" ><![CDATA[.base_triangle
+{
+	stroke: black;
+	stroke-width: 1px;
+}
+
+.third_tri
+{
+	fill: yellowgreen;
+}
+
+.first_tri
+{
+	fill: skyblue;
+}
+
+.overlap_clip
+{
+	clip-path: url(#overlap);
+}
+
+.second_tri
+{
+	fill: orange;
+}]]></style>
+	<defs >
+		<clipPath id="overlap" >
+			<rect y="0" x="0" height="125" width="250" class="show_clip" />
+		</clipPath>
+	</defs>
+	<polygon points="175,25 62.5,231.25 31.25,200" class="base_triangle first_tri" />
+	<polygon points="12.5,175 231.25,162.5 218.75,206.25" class="base_triangle second_tri" />
+	<polygon points="200,237.5 137.5,12.5 100,37.5" class="base_triangle third_tri" />
+	<polygon points="175,25 62.5,231.25 31.25,200" class="base_triangle first_tri overlap_clip" />
+</svg>

Documents/Positioning/Tutorial 04.xml

                 </imageobject>
             </mediaobject>
         </equation>
-        <para>This expresses each clip space position as the sum of factors of
-                <emphasis>all</emphasis> of the camera space position components. Most of the terms
-            are zero, of course, so they don't contribute to the output. But they do contribute to
-            being able to re-express the entire transformation as a single operation.</para>
+        <para>What we have here is what is known as a linear system of equations. The equations can
+            be specified as a series of coefficients (the numbers being multiplied by the XYZW
+            values) which are multiplied by the input values (XYZW) to produce the single output.
+            Each individual output value is a linear combination of all of the input values. In our
+            case, there just happen to be a lot of zero coefficients, so the output values in this
+            particular case only depend on a few input values.</para>
         <para>You may be wondering at the multiplication of the additive term of
                 Z<subscript>clip</subscript>'s value by the camera space W. Well, our input camera
             space position's W coordinate is always 1. So performing the multiplication is valid, so
             long as this continues to be the case. Being able to do what we are about to do is part
             of the reason why the W coordinate exists in our camera-space position values (the perspective divide is the other).</para>
-			<!--TODO: Talk about a linear system of equations. -->
-        <para>Let us now re-express this again, using the coefficients of the equation above. You
-            may recognize this reformulation, depending on your knowledge of linear algebra:</para>
+        <para>We can re-express any linear system of equations using a special kind of formulation.
+            You may recognize this reformulation, depending on your knowledge of linear
+            algebra:</para>
         <equation>
             <title>Camera to Clip Matrix Transformation</title>
             <mediaobject>
         </equation>
         <para>The two long vertical columns of XYZW labeled <quote>clip</quote> and
                 <quote>camera</quote> are 4-dimensional vectors; namely the clip and camera space
-            vectors. The larger block of numbers is a matrix. You probably are not familiar with
-            matrix math. If not, it will be explained presently.</para>
+            position vectors. The larger block of numbers is a matrix. You probably are not familiar
+            with matrix math. If not, it will be explained presently.</para>
         <para>Generically speaking, a <glossterm>matrix</glossterm> is a two dimensional block of
             numbers (matrices with more than 2 dimensions are called <quote>tensors</quote>).
             Matrices are very common in computer graphics. Thus far, we have been able to get along
             the matrix, the values of each component of the column are multiplied by the
             corresponding values in the rows of the vector. These values are then added together;
             that becomes the single value for the row of the output vector.</para>
+        <equation>
+            <title>Vector Matrix Multiplication</title>
+            <mediaobject>
+                <imageobject>
+                    <imagedata fileref="VectorMatrixMultiplication.svg" format="SVG"/>
+                </imageobject>
+            </mediaobject>
+        </equation>
         <para>This results ultimately in performing 16 floating-point multiplications and 12
             floating-point additions. That's quite a lot, particularly compared with our current
             version. Fortunately, graphics hardware is designed to make these operations very fast.
-            Because each of the multiplications are independent of each other, they can all be done
-            simultaneously, which is exactly the kind of thing graphics hardware does fast.
+            Because each of the multiplications are independent of each other, they could all be
+            done simultaneously, which is exactly the kind of thing graphics hardware does fast.
             Similarly, the addition operations are partially independent; each row's summation
-            doesn't depend on the values from any other row.</para>
+            doesn't depend on the values from any other row. Ultimately, vector-matrix
+            multiplication usually generates only 4 instructions in the GPU's machine
+            language.</para>
         <para>We can re-implement the above perspective projection using matrix math rather than
             explicit math. The <phrase role="propername">MatrixPerspective</phrase> tutorial does
             this.</para>
             </listitem>
             <listitem>
                 <para>The perspective transformation can be performed as a matrix multiplication
-                    operation.</para>
+                    operation. Matrix/vector multiplication is a way to compute multiple linear
+                    equations in a single operation.</para>
             </listitem>
             <listitem>
                 <para>The proper aspect ratio for a display image can be maintained by scaling the X

Documents/Positioning/Tutorial 05.xml

             imagine, this <quote>solution</quote> scales incredibly poorly. Doing it for each
             triangle is prohibitive, particularly with scenes with millions of triangles.</para>
         <para>And the worst part is that even if you put in all the effort, it doesn't actually
-            work. Not all the time. Many trivial cases can be solved via depth sorting, but
-            non-trivial cases have real problems. You can have an arrangement of 3 triangles where
-            each overlaps the other, such that there simply is no order you can render them in to
-            achieve the right effect.</para>
-			<!--TODO: Show the 3 triangle arrangement.-->
-        <para>Even worse, it does nothing for interpenetrating triangles; that is, triangles that
+            work. Not all the time at any rate. Many trivial cases can be solved via depth sorting,
+            but non-trivial cases have real problems. You can have an arrangement of 3 triangles
+            where each overlaps the other, such that there simply is no order you can render them in
+            to achieve the right effect.</para>
+        <figure>
+            <title>Three Overlapping Triangles</title>
+            <mediaobject>
+                <imageobject>
+                    <imagedata fileref="ThreeTriangleOverlap.svg" format="SVG"/>
+                </imageobject>
+            </mediaobject>
+        </figure>
+        <para>Even worse, it does nothing for inter-penetrating triangles; that is, triangles that
             pass through each other in 3D space (as opposed to just from the perspective of the
             camera).</para>
         <para>Depth sorting isn't going to cut it; clearly, we need something better.</para>

Documents/Positioning/Tutorial 06.xml

         <para>Throughout this series of tutorials, we have discussed a number of different spaces.
             We have seen OpenGL-defined spaces like normalized device coordinate (NDC) space,
             clip-space, and window space. And we have seen user-defined spaces like camera space.
-            But we have yet to really discuss about what a space actually is.</para>
+            But we have yet to formally discuss about what a space actually is.</para>
         <para>A <glossterm>space</glossterm> is a shorthand term for a <glossterm>coordinate
                 system.</glossterm> For the purposes of this conversation, a coordinate system or
             space consists of the following:</para>
         </figure>
         <para>These are two different coordinate systems. The same coordinate, in this case (2, 2)
             (each basis vector is added twice) can have two very different positions, from the point
-            of view of a neutral observer. What is interesting to note is that (2, 2) is the same in
-            their own coordinate system. This means that a coordinate itself is not enough
-            information to know what it means; one must also know what coordinate system it is
-            in.</para>
+            of view of a neutral observer. What is interesting to note is that (2, 2) is the same
+            value in their own coordinate system. This means that a coordinate itself is not enough
+            information to know what it means; one must also know what coordinate system it is in
+            before you can know anything about it.</para>
         <para>The numerical version of the coordinate system equation is as follows:</para>
         <equation>
             <title>Coordinate System</title>
                 changed; all that has changed is the coordinate system that this coordinate is
                 relative to.</para>
             <para>We have seen a number of coordinate system transformations. OpenGL implements the
-                transformation from clip-space to NDC space, and the transformation from NDC to
+                transformation from clip-space to NDC space and the transformation from NDC to
                 window space. Our shaders implement the transformation from camera space to
                 clip-space, and this was done using a matrix. Perspective projection (and
                 orthographic, for that matter) are simply a special kind of transformation.</para>
         <title>Translation</title>
         <para>The simplest space transformation operation is translation. Indeed, we have not only
             seen this transform before, it has been used in all of the tutorials with a perspective
-            projection. Remember this line from the vertex shaders:</para>
+            projection. Recall this line from the vertex shaders:</para>
         <programlisting language="glsl">vec4 cameraPos = position + vec4(offset.x, offset.y, 0.0, 0.0);</programlisting>
         <para>This is a <glossterm>translation</glossterm> transformation: it is used to position
             the origin point of the initial space relative to the destination space. Since all of
             translation needs to do is add a vector to all of the coordinates in that space. The
             vector added to these values is the location of where the user wants the origin point
             relative to the destination coordinate system.</para>
-        <para>Here is a more concrete example. Let us say that an object which in its model space is
-            near its origin. This means that, if we want to see that object in front of the camera,
-            we must position the origin of the model in front of the camera. If the extent of the
-            model is only [-1, 1] in model space, we can ensure that the object is visible by adding
-            this vector to all of the model space coordinates: (0, 0, -3). This puts the origin of
-            the model at that position in camera space.</para>
         <figure>
             <title>Coordinate System Translation in 2D</title>
             <mediaobject>
                 </imageobject>
             </mediaobject>
         </figure>
+        <para>Here is a more concrete example. Let us say that an object which in its model space is
+            near its origin. This means that, if we want to see that object in front of the camera,
+            we must position the origin of the model in front of the camera. If the extent of the
+            model is only [-1, 1] in model space, we can ensure that the object is visible by adding
+            this vector to all of the model space coordinates: (0, 0, -3). This puts the origin of
+            the model at that position in camera space.</para>
         <para>Translation is ultimately just that simple. So let's make it needlessly complex. And
             the best tool for doing that: matrices. Oh, we could just use a 3D uniform vector to
             pass an offset to do the transformation. But matrices have hidden benefits we will
     glUseProgram(0);
 }</programlisting>
         </example>
-        <para>GLM takes a unique approach, for a vector/matrix math library. It attempts to emulate
+        <para>GLM takes a unique approach for a vector/matrix math library. It attempts to emulate
             GLSL's approach to vector operations where possible. It uses C++ operator overloading to
-            effectively emulate GLSL. In most cases, GLM-based code would compile in GLSL.</para>
+            effectively emulate GLSL. In many cases, GLM-based expressions would compile in
+            GLSL.</para>
         <para>The matrix <varname>cameraToClipMatrix</varname> is defined as a
                 <type>glm::mat4</type>, which has the same properties as a GLSL <type>mat4.</type>
             Array indexing of a <type>mat4</type>, whether GLM or GLSL, returns the zero-based
             </mediaobject>
         </figure>
         <para>Scaling can be uniform, which means each basis vector is scaled by the same value. A
-            non-uniform scale means that each basis can get a different scale, or none at
-            all.</para>
+            non-uniform scale means that each basis can get a different scale or none at all.</para>
         <para>Uniform scales are used to allow objects in model space to have different units from
             the units used in camera space. For example, a modeller may have generated the model in
             inches, but the world uses centimeters. This will require applying a uniform scale to
                 </imageobject>
             </mediaobject>
         </informalequation>
-        <para>Since multiplication is both associative and commutative, we can multiply the scales
-            directly into the coordinate values to achieve the same effect. So a scaled space can be
-            reexpressed as simply multiplying the coordinate values.</para>
+        <para>Since scalar-vector multiplication is both associative and commutative, we can
+            multiply the scales directly into the coordinate values to achieve the same effect. So a
+            scaled space can be reexpressed as simply multiplying the input coordinate
+            values.</para>
         <para>This is easy enough to do in GLSL, if you pass a vector uniform containing the scale
             values. But that's just not complicated enough. Obviously, we need to get matrices
             involved, but how?</para>
         </informalequation>
         <para>This matrix selects each coordinate in turn from the vector it is being multiplied
             into. Each row is multiplied with the column of the vector; all of the zeros remove the
-            components of the vector that we do not want. The one multiplies into the component we
-            do want, thus selecting it. This produces the identity result: the vector we started
-            with.</para>
+            components of the vector that we do not want. The 1 value of each row multiplies into
+            the component we do want, thus selecting it. This produces the identity result: the
+            vector we started with.</para>
         <para>We can see that, if the ones were some other value, we would get a scaled version of
             the original vector, depending on which ones were changed. Thus, a scaling
             transformation matrix looks like this:</para>
             fixed value. We will discuss the ramifications of applying multiple transforms later;
             suffice it to say, this currently works.</para>
         <para>Scaling is only slightly more complicated than translation.</para>
-        <section>
+        <sidebar>
             <title>Perspective and Scaling</title>
             <para>The way we construct a scale transformation matrix may seem familiar to you. Back
                 in Tutorial 4, the perspective transformation involved a frustum scale value. This
                 and Y positions. When we constructed the perspective matrix, we used the frustum
                 scale as a uniform scaling in the X and Y dimensions. The aspect ratio compensation
                 code was nothing more than applying a <emphasis>non</emphasis>uniform scale.</para>
-        </section>
+        </sidebar>
         <section>
             <title>Inversion and Winding Order</title>
             <para>Scales can be theoretically negative, or even 0. A scale of 0 causes the basis
                 vector in that direction to become 0 entirely. An basis vector with no length means
                 that a dimension has effectively been lost. The resulting transform squashes
-                everthing in that direction down to the origin. A 3D space becomes a 2D space (or 1D
-                or 0D, depending on how many axes were scaled).</para>
+                everything in that direction down to the origin. A 3D space becomes a 2D space (or
+                1D or 0D, depending on how many axes were scaled).</para>
             <para>A negative scale changes the direction of an axis. This causes vertices
                 transformed with this scale to flip across the origin in that axis's direction. This
                 is called an <glossterm>inversion</glossterm>. This can have certain unintended
                 </imageobject>
             </mediaobject>
         </figure>
-        <para>Rotations are usually considered the most complex of transformations, primarily
-            because of the math involved in computing the transformation matrix. Generally,
-            rotations are looked at as an operation, such as rotating around a particular basis or
-            some such. The prior part of the tutorial laid down some of the groundwork that will
-            make this much simpler.</para>
+        <para>Rotations are usually considered the most complex of the basic transformations,
+            primarily because of the math involved in computing the transformation matrix.
+            Generally, rotations are looked at as an operation, such as rotating around a particular
+            basis or some such. The prior part of the tutorial laid down some of the groundwork that
+            will make this much simpler.</para>
         <para>First, let's look back at our equation for determining what the position of a
             coordinate is relative to certain coordinate space:</para>
         <informalequation>
         </informalequation>
         <para>Doesn't this look a bit familiar? No? Maybe this look at vector-matrix multiplication
             will jog your memory:</para>
-        <equation>
-            <title>Vector Matrix Multiplication</title>
+        <informalequation>
             <mediaobject>
                 <imageobject>
                     <imagedata fileref="VectorMatrixMultiplication.svg" format="SVG"/>
                 </imageobject>
             </mediaobject>
-        </equation>
+        </informalequation>
         <para>Still nothing? Perhaps an alternate look would help:</para>
         <equation>
             <title>Vectorized Matrix Multiplication</title>
         </equation>
         <para>Does it look familiar <emphasis>now</emphasis>?</para>
         <para>What this tells us is that the columns of our transformation matrices are, and have
-            always been, nothing more than the axes of a coordinate system. Except for the fourth
-            column: because the position has a 1 in the W, it acts as an offset.</para>
+                <emphasis>always</emphasis> been, nothing more than the axes of a coordinate system.
+            Except for the fourth column: because the input position has a 1 in the W, it acts as an
+            offset.</para>
         <para>Transformation ultimately means this: taking the basis vectors and origin point from
             the original coordinate system and re-expressing them relative to the destination
             coordinate system.</para>
             relative to another space. Remember this, and you will avoid many pitfalls when you
             start dealing with more complex transformations.</para>
         <para>For any two spaces, the orientation transformation between then can be expressed as
-            rotating the source space by some angle around a particular axis (also in the initial
-            space). This is true for any change of orientation.</para>
+            rotating the source space by some angle around a particular axis (specified in the
+            initial space). This is true for any change of orientation.</para>
         <para>A common rotation question is to compute a rotation around an arbitrary axis. Or to put it more
             correctly, to determine the orientation of a space if it is rotated around an arbitrary
             axis. The axis of rotation is expressed in terms of the
                 </imageobject>
             </mediaobject>
         </equation>
-        <para>When using the C library <function>sin</function> and <function>cos</function>
-            functions, the angles must be in radians.</para>
+        <para>When using the standard C/C++ library <function>sin</function> and
+                <function>cos</function> functions, the angles must be in radians.</para>
         <para>As useful as these are, the more generic equation for rotation by an angle about an
             arbitrary axis is as follows.</para>
         <equation>
             </mediaobject>
         </equation>
         <para>All of these matrices are such that, from the point of view of an observer looking
-            down the axis of rotation (the direction of the axis is pointed into the eye of the
-            observer), the object rotates counter-clockwise with positive angles.</para>
+            down the axis of rotation (the positive direction of the axis is pointed into the eye of
+            the observer), the object rotates counter-clockwise with positive angles.</para>
         <para>The <phrase role="propername">Rotations</phrase> tutorial shows off each of these
             rotation matrix functions. Similar to how the others work, there are multiple instances
             rendered based on functions.</para>
             primarily so that we could see everything properly.</para>
         <para>But these are not the only combinations of transformations that can be performed.
             Indeed, any combination of transformation operations is possible, though it may not be
-                <emphasis>meaningful.</emphasis></para>
+            meaningful.</para>
         <para>Successive transformations can be seen as doing successive multiplication operations.
             For example, if S is a pure scale matrix, T is a pure translation matrix, and R is a
-            pure scale matrix, then one can easily do the following in a shader:</para>
+            pure scale matrix, then the shader can compute the result of a transformation as
+            follows:</para>
         <programlisting language="glsl">vec4 temp;
 temp = T * position;
 temp = R * temp;
                 Remember: a matrix, even a translation matrix, defines a full-fledged coordinate
                 system.</para>
             <para>So S now acts on the T-space position of the vertices. T-space has an origin,
-                which in T-space is (0, 0, 0), but in model space is the translation part of the
-                matrix T. A scaling transformation matrix performs scaling based on the origin point
-                in the space of the vertices being scaled. So the scaling matrix S will scale the
-                points away from the origin point in T-space. Since what you (probably) actually
-                wanted was to scale the points away from the origin point in <emphasis>model
-                    space</emphasis>, S needs to come first.</para>
-            <para>Rotation (orientation) matrices have the same issue. The orientation is always
+                which in T-space is (0, 0, 0). However, this origin back in model space is the
+                translation part of the matrix T. A scaling transformation matrix performs scaling
+                based on the origin point in the space of the vertices being scaled. So the scaling
+                matrix S will scale the points away from the origin point in T-space. Since what you
+                (probably) actually wanted was to scale the points away from the origin point in
+                    <emphasis>model space</emphasis>, S needs to come first.</para>
+            <para>Orientation (rotation) matrices have the same issue. The orientation is always
                 local to the origin in the current space of the positions. So a rotation matrix must
                 happen before the translation matrix. Scales generally should happen before
                 orientation; if they happen afterwards, then the scale will be relative to the
                 matrix to get into that space, then undo the rotation after applying the
                 scale.</para>
             <para>The general form of this sequence is as follows. Suppose you have a transformation
-                T, that you wish to apply in the space M. The transformation matrix T in the space
-                of M is <inlineequation>
+                matrix T, which operates on points in a space called F. We have some positions in
+                the space P. What we want is to create a matrix that applies T's transformation
+                operation, except that it needs to operate on points in the space of P. Given a
+                matrix M that transforms from P space to F space, that matrix is <inlineequation>
                     <mathphrase>M<superscript>-1</superscript>TM</mathphrase>
                 </inlineequation>.</para>
             <para>The matrix M<superscript>-1</superscript> is the <glossterm>inverse
                     matrix</glossterm> of M. The symbol <superscript>-1</superscript> does not
-                (strictly) mean to raise the matrix to the -1 power. It simply means to invert
-                it.</para>
-            <para>The inverse matrix of M is the matrix N such that <inlineequation>
+                (strictly) mean to raise the matrix to the -1 power. It means to invert it.</para>
+            <para>The inverse of a matrix M is the matrix N such that <inlineequation>
                     <mathphrase>MN = I</mathphrase>
                 </inlineequation>, where I is the identity matrix. This can be analogized to the
                 scalar multiplicative inverse (ie: reciprocal). The scalar multiplicative inverse of
                 X is the number Y such that <inlineequation>
                     <mathphrase>XY = 1</mathphrase>
                 </inlineequation>.</para>
-            <para>In the case of the scalar inverse, this is very easy to compute: <inlineequation>
+            <para>In the case of the scalar inverse, this is very easy to solve for: <inlineequation>
                     <mathphrase>Y = 1/X</mathphrase>
                 </inlineequation>. Even in this case, there are values of X for which there is no
-                multiplicative inverse. OK, there's <emphasis>one</emphasis> value of X: 0.</para>
+                multiplicative inverse. OK, there's <emphasis>one</emphasis> such value of X:
+                0.</para>
             <para>The case of the inverse matrix is much more complicated. Just as with the scalar
                 inverse, there are matrices that have no inverse. Unlike the scalar case, there are
                 a <emphasis>lot</emphasis> of matrices with no inverse. Also, computing the inverse
                 matrix is a <emphasis>lot</emphasis> more complicated than simply taking the
                 reciprocal of a value.</para>
-            <para>Most common transformation matrices <emphasis>do</emphasis> have an inverse. And
-                for the simplest of matrices, the inverse matrix is very easy to compute. For a pure
+            <para>Most common transformation matrices do have an inverse. And for the basic
+                transformation matrices, the inverse matrix is very easy to compute. For a pure
                 rotation matrix, simply compute a new rotation matrix by negating the angle that the
                 old one was generated with. For a translation matrix, negate the origin value in the
                 matrix. For a scale matrix, take the reciprocal of the scale along each axis.</para>
         </section>
         <section>
             <title>Hierarchical Models</title>
-            <para>In more complex scenes, it is often desireable to specify the transform of one
+            <para>In more complex scenes, it is often desirable to specify the transform of one
                 model relative to the model space transform of another model. This is useful if you
                 want one object (object B) to pick up another object (object A). The object that
                 gets picked up needs to follow the transform of the object that picked it up. So it
                 difference between the child functions and the root one is that this function has a
                 push/pop wrapper around the entire thing. Though since the root creates a
                 MatrixStack to begin with, this could be considered the equivalent.</para>
-            <note>
+            <sidebar>
+                <title>Matrix Stack Conventions</title>
                 <para>There are two possible conventions for matrix stack behavior. The caller could
                     be responsible for pushing and popping the matrix, or the callee (the function
                     being called) could be responsible for this. These are called caller-save and
                     object as a non-const reference), so it will have to do a push/pop. Whereas with
                     callee-save, you only push/pop as you explicitly need: at the cite where you are
                     modifying the matrix stack. It groups the code together better.</para>
-            </note>
+            </sidebar>
         </section>
     </section>
     <section>
         <?dbhtml filename="Tut06 Gimbal Lock.html" ?>
         <title>Gimbal Lock</title>
-        <para>Remember back when we said that an rotation matrix isn't a rotation matrix at all,
-            that it is an orientation matrix? We also said that forgetting this can come back to
-            bite you. Well, here's likely the most common way.</para>
+        <para>Remember back when we said that n rotation matrix isn't a rotation matrix at all, that
+            it is an orientation matrix? We also said that forgetting this can come back to bite
+            you. Well, here's likely the most common way.</para>
         <para>Normally, when dealing with orienting an object like a plane or spaceship in 3D space,
             you want to orient it based on 3 rotations about the 3 axes. The obvious way to do this
             is with a series of 3 rotations. This means that the program stores 3 angles, and you
             anytime you compute a final rotation from a series of 3 rotations about axes
             perpendicular to each other.</para>
         <para>The tutorial project <phrase role="propername">Gimbal Lock</phrase> illustrates this
-            problem. Because the problem was first diagnosed with a device called a <link
+            problem. Because the problem was first diagnosed with a physical device called a <link
                 xlink:href="http://en.wikipedia.org/wiki/Gimbal">gimbal</link>, the problem has
             become known as <glossterm>gimbal lock.</glossterm></para>
         <para>A gimbal is a pivoted support that provides the ability to rotate in one axis. A
                 </imageobject>
             </mediaobject>
         </figure>
-        <para>Now, the purpose of the three gimbals is to be able to adjust one of the three angles
-            and orient the object in a particular direction. In a flight-simulation game, the player
-            would have controls that would change their yaw, pitch, and roll. However, look at this
-            picture.</para>
-        <para>The player's theoretical ship is pointed in the direction of the red gimbal's plane.
-            Given the controls you have here, can you cause the red gimbal to rotate around the axis
-            that it is facing? No. You can't even do this somewhat; you can only rotate it in two
-            directions. But we have three gimbals, which means we should have three axes of
-            rotation. Why can't we rotate the red gimbal in the Z (forward) axis?</para>
-        <para>Because the blue and red gimbals are now rotating about the <emphasis>same
+        <para>Recall that the purpose of the three gimbals is to be able to adjust one of the three
+            angles and orient the object in a particular direction. In a flight-simulation game, the
+            player would have controls that would change their yaw, pitch, and roll. However, look
+            at this picture.</para>
+        <para>The player's theoretical ship is pointed in the direction of the center gimbal's
+            plane. Given the controls you have here, can you cause the center gimbal to rotate
+            around the axis that it is facing? No. You can't even do this somewhat; you can only
+            rotate it in two directions. But we have three gimbals, which means we should have three
+            axes of rotation. Why can't we rotate the red gimbal in the Z (forward) axis?</para>
+        <para>Because the outer and inner gimbals are now rotating about the <emphasis>same
                 axis</emphasis>. Which means you really only have two gimbals to manipulate in order
             to orient the red gimbal. And 3D orientation cannot be fully controlled with only 2
             axial rotations, with only 2 gimbals.</para>

Tut 08 Lights on/Ambient Lighting.cpp

 #include "../framework/Mesh.h"
 #include "../framework/MatrixStack.h"
 #include "../framework/MousePole.h"
+#include "../framework/ObjectPole.h"
 #include <glm/glm.hpp>
 #include <glm/gtc/type_ptr.hpp>
 
 Framework::Mesh *g_pPlaneMesh = NULL;
 
 Framework::RadiusDef radiusDef = {5.0f, 3.0f, 20.0f, 1.5f, 0.5f};
-Framework::MousePole g_mousePole(glm::vec3(0.0f, 0.5f, 0.0f), radiusDef);
+glm::vec3 objectCenter = glm::vec3(0.0f, 0.5f, 0.0f);
+
+Framework::MousePole g_mousePole(objectCenter, radiusDef);
+Framework::ObjectPole g_objectPole(objectCenter, &g_mousePole);
 
 namespace
 {
 	void MouseMotion(int x, int y)
 	{
 		g_mousePole.GLUTMouseMove(glm::ivec2(x, y));
+		g_objectPole.GLUTMouseMove(glm::ivec2(x, y));
 		glutPostRedisplay();
 	}
 
 	void MouseButton(int button, int state, int x, int y)
 	{
 		g_mousePole.GLUTMouseButton(button, state, glm::ivec2(x, y));
+		g_objectPole.GLUTMouseButton(button, state, glm::ivec2(x, y));
 		glutPostRedisplay();
 	}
 
 	void MouseWheel(int wheel, int direction, int x, int y)
 	{
 		g_mousePole.GLUTMouseWheel(direction, glm::ivec2(x, y));
+		g_objectPole.GLUTMouseWheel(direction, glm::ivec2(x, y));
 		glutPostRedisplay();
 	}
 }
 
+
 //Called after the window and OpenGL are initialized. Called exactly once, before the main loop.
 void init()
 {
 
 glm::vec4 g_lightDirection(0.866f, 0.5f, 0.0f, 0.0f);
 
-static float g_CylYaw = 0.0f;
-static float g_CylPitch = 0.0f;
-static float g_CylRoll = 0.0f;
-
 static bool g_bDrawColoredCyl = true;
 static bool g_bShowAmbient = false; 
 
 			{
 				Framework::MatrixStackPusher push(modelMatrix);
 
-				modelMatrix.Translate(0.0f, 0.5f, 0.0f);
-
-				modelMatrix.RotateX(g_CylPitch);
-				modelMatrix.RotateY(g_CylYaw);
-				modelMatrix.RotateZ(g_CylRoll);
+				modelMatrix.ApplyMatrix(g_objectPole.CalcMatrix());
 
 				if(g_bDrawColoredCyl)
 				{
 		delete g_pCylinderMesh;
 		glutLeaveMainLoop();
 		break;
-	case 'w': g_CylPitch -= 11.25f; break;
-	case 's': g_CylPitch += 11.25f; break;
-	case 'd': g_CylRoll -= 11.25f; break;
-	case 'a': g_CylRoll += 11.25f; break;
-	case 'e': g_CylYaw -= 11.25f; break;
-	case 'q': g_CylYaw += 11.25f; break;
-	case 'W': g_CylPitch -= 4.0f; break;
-	case 'S': g_CylPitch += 4.0f; break;
-	case 'D': g_CylRoll -= 4.0f; break;
-	case 'A': g_CylRoll += 4.0f; break;
-	case 'E': g_CylYaw -= 4.0f; break;
-	case 'Q': g_CylYaw += 4.0f; break;
-		
+
 	case 32:
 		g_bDrawColoredCyl = !g_bDrawColoredCyl;
-		printf("Yaw: %f, Pitch: %f, Roll: %f\n", g_CylYaw, g_CylPitch, g_CylRoll);
 		break;
 
 	case 't':

Tut 08 Lights on/Basic Lighting.cpp

 #include "../framework/Mesh.h"
 #include "../framework/MatrixStack.h"
 #include "../framework/MousePole.h"
+#include "../framework/ObjectPole.h"
 #include <glm/glm.hpp>
 #include <glm/gtc/type_ptr.hpp>
 
 Framework::Mesh *g_pCylinderMesh = NULL;
 Framework::Mesh *g_pPlaneMesh = NULL;
 Framework::RadiusDef radiusDef = {5.0f, 3.0f, 20.0f, 1.5f, 0.5f};
-Framework::MousePole g_mousePole(glm::vec3(0.0f, 0.5f, 0.0f), radiusDef);
+
+glm::vec3 objectCenter = glm::vec3(0.0f, 0.5f, 0.0f);
+
+Framework::MousePole g_mousePole(objectCenter, radiusDef);
+Framework::ObjectPole g_objectPole(objectCenter, &g_mousePole);
 
 namespace
 {
 	void MouseMotion(int x, int y)
 	{
 		g_mousePole.GLUTMouseMove(glm::ivec2(x, y));
+		g_objectPole.GLUTMouseMove(glm::ivec2(x, y));
 		glutPostRedisplay();
 	}
 
 	void MouseButton(int button, int state, int x, int y)
 	{
 		g_mousePole.GLUTMouseButton(button, state, glm::ivec2(x, y));
+		g_objectPole.GLUTMouseButton(button, state, glm::ivec2(x, y));
 		glutPostRedisplay();
 	}
 
 	void MouseWheel(int wheel, int direction, int x, int y)
 	{
 		g_mousePole.GLUTMouseWheel(direction, glm::ivec2(x, y));
+		g_objectPole.GLUTMouseWheel(direction, glm::ivec2(x, y));
 		glutPostRedisplay();
 	}
 }
 
 glm::vec4 g_lightDirection(0.866f, 0.5f, 0.0f, 0.0f);
 
-static float g_CylYaw = 0.0f;
-static float g_CylPitch = 0.0f;
-static float g_CylRoll = 0.0f;
-
 static bool g_bDrawColoredCyl = true;
 
 //Called to update the display.
 			{
 				Framework::MatrixStackPusher push(modelMatrix);
 
-				modelMatrix.Translate(0.0f, 0.5f, 0.0f);
-
-				modelMatrix.RotateX(g_CylPitch);
-				modelMatrix.RotateY(g_CylYaw);
-				modelMatrix.RotateZ(g_CylRoll);
+				modelMatrix.ApplyMatrix(g_objectPole.CalcMatrix());
 
 				if(g_bDrawColoredCyl)
 				{
 		delete g_pCylinderMesh;
 		glutLeaveMainLoop();
 		break;
-	case 'w': g_CylPitch -= 11.25f; break;
-	case 's': g_CylPitch += 11.25f; break;
-	case 'd': g_CylRoll -= 11.25f; break;
-	case 'a': g_CylRoll += 11.25f; break;
-	case 'e': g_CylYaw -= 11.25f; break;
-	case 'q': g_CylYaw += 11.25f; break;
-	case 'W': g_CylPitch -= 4.0f; break;
-	case 'S': g_CylPitch += 4.0f; break;
-	case 'D': g_CylRoll -= 4.0f; break;
-	case 'A': g_CylRoll += 4.0f; break;
-	case 'E': g_CylYaw -= 4.0f; break;
-	case 'Q': g_CylYaw += 4.0f; break;
 		
 	case 32:
 		g_bDrawColoredCyl = !g_bDrawColoredCyl;
-		printf("Yaw: %f, Pitch: %f, Roll: %f\n", g_CylYaw, g_CylPitch, g_CylRoll);
 		break;
 	}
 

Tut 08 Lights on/Scale and Lighting.cpp

 #include "../framework/Mesh.h"
 #include "../framework/MatrixStack.h"
 #include "../framework/MousePole.h"
+#include "../framework/ObjectPole.h"
 #include <glm/glm.hpp>
 #include <glm/gtc/type_ptr.hpp>
 
 Framework::Mesh *g_pCylinderMesh = NULL;
 Framework::Mesh *g_pPlaneMesh = NULL;
 Framework::RadiusDef radiusDef = {5.0f, 3.0f, 20.0f, 1.5f, 0.5f};
-Framework::MousePole g_mousePole(glm::vec3(0.0f, 0.5f, 0.0f), radiusDef);
+glm::vec3 objectCenter = glm::vec3(0.0f, 0.5f, 0.0f);
+
+Framework::MousePole g_mousePole(objectCenter, radiusDef);
+Framework::ObjectPole g_objectPole(objectCenter, &g_mousePole);
 
 namespace
 {
 	void MouseMotion(int x, int y)
 	{
 		g_mousePole.GLUTMouseMove(glm::ivec2(x, y));
+		g_objectPole.GLUTMouseMove(glm::ivec2(x, y));
 		glutPostRedisplay();
 	}
 
 	void MouseButton(int button, int state, int x, int y)
 	{
 		g_mousePole.GLUTMouseButton(button, state, glm::ivec2(x, y));
+		g_objectPole.GLUTMouseButton(button, state, glm::ivec2(x, y));
 		glutPostRedisplay();
 	}
 
 	void MouseWheel(int wheel, int direction, int x, int y)
 	{
 		g_mousePole.GLUTMouseWheel(direction, glm::ivec2(x, y));
+		g_objectPole.GLUTMouseWheel(direction, glm::ivec2(x, y));
 		glutPostRedisplay();
 	}
 }
 
 glm::vec4 g_lightDirection(0.866f, 0.5f, 0.0f, 0.0f);
 
-static float g_CylYaw = 0.0f;
-static float g_CylPitch = 0.0f;
-static float g_CylRoll = 0.0f;
-
 static bool g_bScaleCyl = false;
 static bool g_bDoInvTranspose = true; 
 
 			{
 				Framework::MatrixStackPusher push(modelMatrix);
 
-				modelMatrix.Translate(0.0f, 0.5f, 0.0f);
-
-				modelMatrix.RotateX(g_CylPitch);
-				modelMatrix.RotateY(g_CylYaw);
-				modelMatrix.RotateZ(g_CylRoll);
+				modelMatrix.ApplyMatrix(g_objectPole.CalcMatrix());
 
 				if(g_bScaleCyl)
-				{
 					modelMatrix.Scale(1.0f, 1.0f, 0.2f);
-				}
 
 				glUseProgram(g_VertexDiffuseColor.theProgram);
 				glUniformMatrix4fv(g_VertexDiffuseColor.modelToCameraMatrixUnif, 1, GL_FALSE, glm::value_ptr(modelMatrix.Top()));
 		delete g_pCylinderMesh;
 		glutLeaveMainLoop();
 		break;
-	case 'w': g_CylPitch -= 11.25f; break;
-	case 's': g_CylPitch += 11.25f; break;
-	case 'd': g_CylRoll -= 11.25f; break;
-	case 'a': g_CylRoll += 11.25f; break;
-	case 'e': g_CylYaw -= 11.25f; break;
-	case 'q': g_CylYaw += 11.25f; break;
-	case 'W': g_CylPitch -= 4.0f; break;
-	case 'S': g_CylPitch += 4.0f; break;
-	case 'D': g_CylRoll -= 4.0f; break;
-	case 'A': g_CylRoll += 4.0f; break;
-	case 'E': g_CylYaw -= 4.0f; break;
-	case 'Q': g_CylYaw += 4.0f; break;
 
 	case 32:
 		g_bScaleCyl = !g_bScaleCyl;
-		printf("Yaw: %f, Pitch: %f, Roll: %f\n", g_CylYaw, g_CylPitch, g_CylRoll);
 		break;
 
 	case 't':

Tut 09 Plane Lights/Fragment Attenuation.cpp

 #include "../framework/Mesh.h"
 #include "../framework/MatrixStack.h"
 #include "../framework/MousePole.h"
+#include "../framework/ObjectPole.h"
 #include <glm/glm.hpp>
 #include <glm/gtc/type_ptr.hpp>
 
 Framework::Mesh *g_pCubeMesh = NULL;
 
 Framework::RadiusDef radiusDef = {5.0f, 3.0f, 200.0f, 1.5f, 0.5f};
-Framework::MousePole g_mousePole(glm::vec3(0.0f, 0.5f, 0.0f), radiusDef);
+glm::vec3 objectCenter = glm::vec3(0.0f, 0.5f, 0.0f);
+
+Framework::MousePole g_mousePole(objectCenter, radiusDef);
+Framework::ObjectPole g_objectPole(objectCenter, &g_mousePole);
 
 namespace
 {
 	void MouseMotion(int x, int y)
 	{
 		g_mousePole.GLUTMouseMove(glm::ivec2(x, y));
+		g_objectPole.GLUTMouseMove(glm::ivec2(x, y));
 		glutPostRedisplay();
 	}
 
 	void MouseButton(int button, int state, int x, int y)
 	{
 		g_mousePole.GLUTMouseButton(button, state, glm::ivec2(x, y));
+		g_objectPole.GLUTMouseButton(button, state, glm::ivec2(x, y));
 		glutPostRedisplay();
 	}
 
 	void MouseWheel(int wheel, int direction, int x, int y)
 	{
 		g_mousePole.GLUTMouseWheel(direction, glm::ivec2(x, y));
+		g_objectPole.GLUTMouseWheel(direction, glm::ivec2(x, y));
 		glutPostRedisplay();
 	}
 }
 	return ret;
 }
 
-static float g_CylYaw = 0.0f;
-static float g_CylPitch = 0.0f;
-static float g_CylRoll = 0.0f;
-
 static bool g_bUseFragmentLighting = true;
 static bool g_bDrawColoredCyl = false;
 static bool g_bDrawLight = false;
 			{
 				Framework::MatrixStackPusher push(modelMatrix);
 
-				modelMatrix.Translate(0.0f, 0.5f, 0.0f);
-
-				modelMatrix.RotateX(g_CylPitch);
-				modelMatrix.RotateY(g_CylYaw);
-				modelMatrix.RotateZ(g_CylRoll);
+				modelMatrix.ApplyMatrix(g_objectPole.CalcMatrix());
 
 				if(g_bScaleCyl)
 					modelMatrix.Scale(1.0f, 1.0f, 0.2f);
 		delete g_pCubeMesh;
 		glutLeaveMainLoop();
 		break;
-	case 'w': g_CylPitch -= 11.25f; break;
-	case 's': g_CylPitch += 11.25f; break;
-	case 'd': g_CylRoll -= 11.25f; break;
-	case 'a': g_CylRoll += 11.25f; break;
-	case 'e': g_CylYaw -= 11.25f; break;
-	case 'q': g_CylYaw += 11.25f; break;
-	case 'W': g_CylPitch -= 4.0f; break;
-	case 'S': g_CylPitch += 4.0f; break;
-	case 'D': g_CylRoll -= 4.0f; break;
-	case 'A': g_CylRoll += 4.0f; break;
-	case 'E': g_CylYaw -= 4.0f; break;
-	case 'Q': g_CylYaw += 4.0f; break;
 		
 	case 32:
 		g_bDrawColoredCyl = !g_bDrawColoredCyl;
-		printf("Yaw: %f, Pitch: %f, Roll: %f\n", g_CylYaw, g_CylPitch, g_CylRoll);
 		break;
 
 	case 'i': g_fLightHeight += 0.2f; break;

Tut 09 Plane Lights/Fragment Point Lighting.cpp

 #include "../framework/Mesh.h"
 #include "../framework/MatrixStack.h"
 #include "../framework/MousePole.h"
+#include "../framework/ObjectPole.h"
 #include <glm/glm.hpp>
 #include <glm/gtc/type_ptr.hpp>
 
 Framework::Mesh *g_pCubeMesh = NULL;
 
 Framework::RadiusDef radiusDef = {5.0f, 3.0f, 200.0f, 1.5f, 0.5f};
-Framework::MousePole g_mousePole(glm::vec3(0.0f, 0.5f, 0.0f), radiusDef);
+glm::vec3 objectCenter = glm::vec3(0.0f, 0.5f, 0.0f);
+
+Framework::MousePole g_mousePole(objectCenter, radiusDef);
+Framework::ObjectPole g_objectPole(objectCenter, &g_mousePole);
 
 namespace
 {
 	void MouseMotion(int x, int y)
 	{
 		g_mousePole.GLUTMouseMove(glm::ivec2(x, y));
+		g_objectPole.GLUTMouseMove(glm::ivec2(x, y));
 		glutPostRedisplay();
 	}
 
 	void MouseButton(int button, int state, int x, int y)
 	{
 		g_mousePole.GLUTMouseButton(button, state, glm::ivec2(x, y));
+		g_objectPole.GLUTMouseButton(button, state, glm::ivec2(x, y));
 		glutPostRedisplay();
 	}
 
 	void MouseWheel(int wheel, int direction, int x, int y)
 	{
 		g_mousePole.GLUTMouseWheel(direction, glm::ivec2(x, y));
+		g_objectPole.GLUTMouseWheel(direction, glm::ivec2(x, y));
 		glutPostRedisplay();
 	}
 }
 	return ret;
 }
 
-static float g_CylYaw = 0.0f;
-static float g_CylPitch = 0.0f;
-static float g_CylRoll = 0.0f;
-
 static bool g_bUseFragmentLighting = true;
 static bool g_bDrawColoredCyl = false;
 static bool g_bDrawLight = false;
 			{
 				Framework::MatrixStackPusher push(modelMatrix);
 
-				modelMatrix.Translate(0.0f, 0.5f, 0.0f);
-
-				modelMatrix.RotateX(g_CylPitch);
-				modelMatrix.RotateY(g_CylYaw);
-				modelMatrix.RotateZ(g_CylRoll);
+				modelMatrix.ApplyMatrix(g_objectPole.CalcMatrix());
 
 				if(g_bScaleCyl)
 					modelMatrix.Scale(1.0f, 1.0f, 0.2f);
 		delete g_pCubeMesh;
 		glutLeaveMainLoop();
 		break;
-	case 'w': g_CylPitch -= 11.25f; break;
-	case 's': g_CylPitch += 11.25f; break;
-	case 'd': g_CylRoll -= 11.25f; break;
-	case 'a': g_CylRoll += 11.25f; break;
-	case 'e': g_CylYaw -= 11.25f; break;
-	case 'q': g_CylYaw += 11.25f; break;
-	case 'W': g_CylPitch -= 4.0f; break;
-	case 'S': g_CylPitch += 4.0f; break;
-	case 'D': g_CylRoll -= 4.0f; break;
-	case 'A': g_CylRoll += 4.0f; break;
-	case 'E': g_CylYaw -= 4.0f; break;
-	case 'Q': g_CylYaw += 4.0f; break;
 		
 	case 32:
 		g_bDrawColoredCyl = !g_bDrawColoredCyl;
-		printf("Yaw: %f, Pitch: %f, Roll: %f\n", g_CylYaw, g_CylPitch, g_CylRoll);
 		break;
 
 	case 'i': g_fLightHeight += 0.2f; break;

Tut 09 Plane Lights/Vertex Point Lighting.cpp

 #include "../framework/Mesh.h"
 #include "../framework/MatrixStack.h"
 #include "../framework/MousePole.h"
+#include "../framework/ObjectPole.h"
 #include <glm/glm.hpp>
 #include <glm/gtc/type_ptr.hpp>
 
 Framework::Mesh *g_pCubeMesh = NULL;
 
 Framework::RadiusDef radiusDef = {5.0f, 3.0f, 20.0f, 1.5f, 0.5f};
-Framework::MousePole g_mousePole(glm::vec3(0.0f, 0.5f, 0.0f), radiusDef);
+glm::vec3 objectCenter = glm::vec3(0.0f, 0.5f, 0.0f);
+
+Framework::MousePole g_mousePole(objectCenter, radiusDef);
+Framework::ObjectPole g_objectPole(objectCenter, &g_mousePole);
 
 namespace
 {
 	void MouseMotion(int x, int y)
 	{
 		g_mousePole.GLUTMouseMove(glm::ivec2(x, y));
+		g_objectPole.GLUTMouseMove(glm::ivec2(x, y));
 		glutPostRedisplay();
 	}
 
 	void MouseButton(int button, int state, int x, int y)
 	{
 		g_mousePole.GLUTMouseButton(button, state, glm::ivec2(x, y));
+		g_objectPole.GLUTMouseButton(button, state, glm::ivec2(x, y));
 		glutPostRedisplay();
 	}
 
 	void MouseWheel(int wheel, int direction, int x, int y)
 	{
 		g_mousePole.GLUTMouseWheel(direction, glm::ivec2(x, y));
+		g_objectPole.GLUTMouseWheel(direction, glm::ivec2(x, y));
 		glutPostRedisplay();
 	}
 }
 	return ret;
 }
 
-static float g_CylYaw = 0.0f;
-static float g_CylPitch = 0.0f;
-static float g_CylRoll = 0.0f;
-
 static bool g_bDrawColoredCyl = false;
 static bool g_bDrawLight = false;
 
 			{
 				Framework::MatrixStackPusher push(modelMatrix);
 
-				modelMatrix.Translate(0.0f, 0.5f, 0.0f);
-
-				modelMatrix.RotateX(g_CylPitch);
-				modelMatrix.RotateY(g_CylYaw);
-				modelMatrix.RotateZ(g_CylRoll);
+				modelMatrix.ApplyMatrix(g_objectPole.CalcMatrix());
 
 				if(g_bDrawColoredCyl)
 				{
 		delete g_pCubeMesh;
 		glutLeaveMainLoop();
 		break;
-	case 'w': g_CylPitch -= 11.25f; break;
-	case 's': g_CylPitch += 11.25f; break;
-	case 'd': g_CylRoll -= 11.25f; break;
-	case 'a': g_CylRoll += 11.25f; break;
-	case 'e': g_CylYaw -= 11.25f; break;
-	case 'q': g_CylYaw += 11.25f; break;
-	case 'W': g_CylPitch -= 4.0f; break;
-	case 'S': g_CylPitch += 4.0f; break;
-	case 'D': g_CylRoll -= 4.0f; break;
-	case 'A': g_CylRoll += 4.0f; break;
-	case 'E': g_CylYaw -= 4.0f; break;
-	case 'Q': g_CylYaw += 4.0f; break;
 		
 	case 32:
 		g_bDrawColoredCyl = !g_bDrawColoredCyl;
-		printf("Yaw: %f, Pitch: %f, Roll: %f\n", g_CylYaw, g_CylPitch, g_CylRoll);
 		break;
 
 	case 'i': g_fLightHeight += 0.2f; break;

Tut 10 Shinies/Blinn vs Phong Lighting.cpp

 #include "../framework/Mesh.h"
 #include "../framework/MatrixStack.h"
 #include "../framework/MousePole.h"
+#include "../framework/ObjectPole.h"
 #include <glm/glm.hpp>
 #include <glm/gtc/type_ptr.hpp>
 
 Framework::Mesh *g_pCubeMesh = NULL;
 
 Framework::RadiusDef radiusDef = {5.0f, 3.0f, 200.0f, 1.5f, 0.5f};
-Framework::MousePole g_mousePole(glm::vec3(0.0f, 0.5f, 0.0f), radiusDef);
+glm::vec3 objectCenter = glm::vec3(0.0f, 0.5f, 0.0f);
+
+Framework::MousePole g_mousePole(objectCenter, radiusDef);
+Framework::ObjectPole g_objectPole(objectCenter, &g_mousePole);
 
 namespace
 {
 	void MouseMotion(int x, int y)
 	{
 		g_mousePole.GLUTMouseMove(glm::ivec2(x, y));
+		g_objectPole.GLUTMouseMove(glm::ivec2(x, y));
 		glutPostRedisplay();
 	}
 
 	void MouseButton(int button, int state, int x, int y)
 	{
 		g_mousePole.GLUTMouseButton(button, state, glm::ivec2(x, y));
+		g_objectPole.GLUTMouseButton(button, state, glm::ivec2(x, y));
 		glutPostRedisplay();
 	}
 
 	void MouseWheel(int wheel, int direction, int x, int y)
 	{
 		g_mousePole.GLUTMouseWheel(direction, glm::ivec2(x, y));
+		g_objectPole.GLUTMouseWheel(direction, glm::ivec2(x, y));
 		glutPostRedisplay();
 	}
 }
 
+
 //Called after the window and OpenGL are initialized. Called exactly once, before the main loop.
 void init()
 {
 	return ret;
 }
 
-static float g_CylYaw = 0.0f;
-static float g_CylPitch = -90.0f;
-static float g_CylRoll = 0.0f;
-
 static int g_eLightModel = LM_BLINN_SPECULAR;
 
 static bool g_bUseFragmentLighting = true;
 			{
 				Framework::MatrixStackPusher push(modelMatrix);
 
-				modelMatrix.Translate(0.0f, 0.5f, 0.0f);
-
-				modelMatrix.RotateX(g_CylPitch);
-				modelMatrix.RotateY(g_CylYaw);
-				modelMatrix.RotateZ(g_CylRoll);
+				modelMatrix.ApplyMatrix(g_objectPole.CalcMatrix());
 
 				if(g_bScaleCyl)
 					modelMatrix.Scale(1.0f, 1.0f, 0.2f);
 		delete g_pCubeMesh;
 		glutLeaveMainLoop();
 		break;
-	case 'w': g_CylPitch -= 11.25f; break;
-	case 's': g_CylPitch += 11.25f; break;
-	case 'd': g_CylRoll -= 11.25f; break;
-	case 'a': g_CylRoll += 11.25f; break;
-	case 'e': g_CylYaw -= 11.25f; break;
-	case 'q': g_CylYaw += 11.25f; break;
-	case 'W': g_CylPitch -= 4.0f; break;
-	case 'S': g_CylPitch += 4.0f; break;
-	case 'D': g_CylRoll -= 4.0f; break;
-	case 'A': g_CylRoll += 4.0f; break;
-	case 'E': g_CylYaw -= 4.0f; break;
-	case 'Q': g_CylYaw += 4.0f; break;
 
 	case 32:
 		g_bDrawColoredCyl = !g_bDrawColoredCyl;
-		printf("Yaw: %f, Pitch: %f, Roll: %f\n", g_CylYaw, g_CylPitch, g_CylRoll);
 		break;
 
 	case 'i': g_fLightHeight += 0.2f; break;

Tut 10 Shinies/Gaussian Specular Lighting.cpp

 #include "../framework/Mesh.h"
 #include "../framework/MatrixStack.h"
 #include "../framework/MousePole.h"
+#include "../framework/ObjectPole.h"
 #include <glm/glm.hpp>
 #include <glm/gtc/type_ptr.hpp>
 
 Framework::Mesh *g_pCubeMesh = NULL;
 
 Framework::RadiusDef radiusDef = {5.0f, 3.0f, 200.0f, 1.5f, 0.5f};
-Framework::MousePole g_mousePole(glm::vec3(0.0f, 0.5f, 0.0f), radiusDef);
+glm::vec3 objectCenter = glm::vec3(0.0f, 0.5f, 0.0f);
+
+Framework::MousePole g_mousePole(objectCenter, radiusDef);
+Framework::ObjectPole g_objectPole(objectCenter, &g_mousePole);
 
 namespace
 {
 	void MouseMotion(int x, int y)
 	{
 		g_mousePole.GLUTMouseMove(glm::ivec2(x, y));
+		g_objectPole.GLUTMouseMove(glm::ivec2(x, y));
 		glutPostRedisplay();
 	}
 
 	void MouseButton(int button, int state, int x, int y)
 	{
 		g_mousePole.GLUTMouseButton(button, state, glm::ivec2(x, y));
+		g_objectPole.GLUTMouseButton(button, state, glm::ivec2(x, y));
 		glutPostRedisplay();
 	}
 
 	void MouseWheel(int wheel, int direction, int x, int y)
 	{
 		g_mousePole.GLUTMouseWheel(direction, glm::ivec2(x, y));
+		g_objectPole.GLUTMouseWheel(direction, glm::ivec2(x, y));
 		glutPostRedisplay();
 	}
 }
 	return ret;
 }
 
-static float g_CylYaw = 0.0f;
-static float g_CylPitch = -90.0f;
-static float g_CylRoll = 0.0f;
-
 static int g_eLightModel = LM_GAUSSIAN_SPECULAR;
 
 bool IsGuassianLightModel()
 			{
 				Framework::MatrixStackPusher push(modelMatrix);
 
-				modelMatrix.Translate(0.0f, 0.5f, 0.0f);
-
-				modelMatrix.RotateX(g_CylPitch);
-				modelMatrix.RotateY(g_CylYaw);
-				modelMatrix.RotateZ(g_CylRoll);
+				modelMatrix.ApplyMatrix(g_objectPole.CalcMatrix());
 
 				if(g_bScaleCyl)
 					modelMatrix.Scale(1.0f, 1.0f, 0.2f);
 		delete g_pCubeMesh;
 		glutLeaveMainLoop();
 		break;
-	case 'w': g_CylPitch -= 11.25f; break;
-	case 's': g_CylPitch += 11.25f; break;
-	case 'd': g_CylRoll -= 11.25f; break;
-	case 'a': g_CylRoll += 11.25f; break;
-	case 'e': g_CylYaw -= 11.25f; break;
-	case 'q': g_CylYaw += 11.25f; break;
-	case 'W': g_CylPitch -= 4.0f; break;
-	case 'S': g_CylPitch += 4.0f; break;
-	case 'D': g_CylRoll -= 4.0f; break;
-	case 'A': g_CylRoll += 4.0f; break;
-	case 'E': g_CylYaw -= 4.0f; break;
-	case 'Q': g_CylYaw += 4.0f; break;
 		
 	case 32:
 		g_bDrawColoredCyl = !g_bDrawColoredCyl;
-		printf("Yaw: %f, Pitch: %f, Roll: %f\n", g_CylYaw, g_CylPitch, g_CylRoll);
 		break;
 
 	case 'i': g_fLightHeight += 0.2f; break;

Tut 10 Shinies/Phong Lighting.cpp

 #include "../framework/Mesh.h"
 #include "../framework/MatrixStack.h"
 #include "../framework/MousePole.h"
+#include "../framework/ObjectPole.h"
 #include <glm/glm.hpp>
 #include <glm/gtc/type_ptr.hpp>
 
 Framework::Mesh *g_pCubeMesh = NULL;
 
 Framework::RadiusDef radiusDef = {5.0f, 3.0f, 200.0f, 1.5f, 0.5f};
-Framework::MousePole g_mousePole(glm::vec3(0.0f, 0.5f, 0.0f), radiusDef);
+glm::vec3 objectCenter = glm::vec3(0.0f, 0.5f, 0.0f);
+
+Framework::MousePole g_mousePole(objectCenter, radiusDef);
+Framework::ObjectPole g_objectPole(objectCenter, &g_mousePole);
 
 namespace
 {
 	void MouseMotion(int x, int y)
 	{
 		g_mousePole.GLUTMouseMove(glm::ivec2(x, y));
+		g_objectPole.GLUTMouseMove(glm::ivec2(x, y));
 		glutPostRedisplay();
 	}
 
 	void MouseButton(int button, int state, int x, int y)
 	{
 		g_mousePole.GLUTMouseButton(button, state, glm::ivec2(x, y));
+		g_objectPole.GLUTMouseButton(button, state, glm::ivec2(x, y));
 		glutPostRedisplay();
 	}
 
 	void MouseWheel(int wheel, int direction, int x, int y)
 	{
 		g_mousePole.GLUTMouseWheel(direction, glm::ivec2(x, y));
+		g_objectPole.GLUTMouseWheel(direction, glm::ivec2(x, y));
 		glutPostRedisplay();
 	}
 }
 	return ret;
 }
 
-static float g_CylYaw = 0.0f;
-static float g_CylPitch = -90.0f;
-static float g_CylRoll = 0.0f;
-
 enum LightingModel
 {
 	LM_PURE_DIFFUSE = 0,
 			{
 				Framework::MatrixStackPusher push(modelMatrix);
 
-				modelMatrix.Translate(0.0f, 0.5f, 0.0f);
-
-				modelMatrix.RotateX(g_CylPitch);
-				modelMatrix.RotateY(g_CylYaw);
-				modelMatrix.RotateZ(g_CylRoll);
+				modelMatrix.ApplyMatrix(g_objectPole.CalcMatrix());
 
 				if(g_bScaleCyl)
 					modelMatrix.Scale(1.0f, 1.0f, 0.2f);
 		delete g_pCubeMesh;
 		glutLeaveMainLoop();
 		break;
-	case 'w': g_CylPitch -= 11.25f; break;
-	case 's': g_CylPitch += 11.25f; break;
-	case 'd': g_CylRoll -= 11.25f; break;
-	case 'a': g_CylRoll += 11.25f; break;
-	case 'e': g_CylYaw -= 11.25f; break;
-	case 'q': g_CylYaw += 11.25f; break;
-	case 'W': g_CylPitch -= 4.0f; break;
-	case 'S': g_CylPitch += 4.0f; break;
-	case 'D': g_CylRoll -= 4.0f; break;
-	case 'A': g_CylRoll += 4.0f; break;
-	case 'E': g_CylYaw -= 4.0f; break;
-	case 'Q': g_CylYaw += 4.0f; break;
 		
 	case 32:
 		g_bDrawColoredCyl = !g_bDrawColoredCyl;
-		printf("Yaw: %f, Pitch: %f, Roll: %f\n", g_CylYaw, g_CylPitch, g_CylRoll);
 		break;
 
 	case 'i': g_fLightHeight += 0.2f; break;

framework/MousePole.cpp

 
 namespace Framework
 {
-	MousePole::MousePole(glm::vec3 target, const RadiusDef &radiusDef)
+	MousePole::MousePole(const glm::vec3 &target, const RadiusDef &radiusDef, ActionButtons eButton)
 		: m_lookAt(target)
 		, m_radCurrXZAngle(0.0)
 		, m_radCurrYAngle(-PI_2 / 2.0f)
 		, m_radCurrSpin(0.0f)
 		, m_fRadius(20.0f)
 		, m_radius(radiusDef)
+		, m_glutActionButton(0)
 		, m_bIsDragging(false)
-	{}
+	{
+		switch(eButton)
+		{
+		case AB_LEFT_MOUSE: m_glutActionButton = GLUT_LEFT_BUTTON; break;
+		case AB_MIDDLE_MOUSE: m_glutActionButton = GLUT_MIDDLE_BUTTON; break;
+		case AB_RIGHT_MOUSE: m_glutActionButton = GLUT_RIGHT_BUTTON; break;
+		}
+	}
 
 	MousePole::~MousePole()
 	{
 			ProcessXChange(iDiff.x);
 			ProcessYChange(iDiff.y);
 			break;
+		case RM_BIAXIAL_ROTATE:
+			if(abs(iDiff.x) > abs(iDiff.y))
+				ProcessXChange(iDiff.x, true);
+			else
+				ProcessYChange(iDiff.y, true);
+			break;
 		case RM_XZ_AXIS_ROTATE:
 			ProcessXChange(iDiff.x);
 			break;
 	const float Y_CONVERSION_FACTOR = PI_2 / SCALE_FACTOR;
 	const float SPIN_CONV_FACTOR = PI_2 / SCALE_FACTOR;
 
-	void MousePole::ProcessXChange(int iXDiff)
+	void MousePole::ProcessXChange( int iXDiff, bool bClearY )
 	{
 		m_radCurrXZAngle = (iXDiff * X_CONVERSION_FACTOR) + m_radInitXZAngle;
+		if(bClearY)
+			m_radCurrYAngle = m_radInitYAngle;
 	}
 
-	void MousePole::ProcessYChange(int iYDiff)
+	void MousePole::ProcessYChange( int iYDiff, bool bClearXZ )
 	{
 		m_radCurrYAngle = (-iYDiff * Y_CONVERSION_FACTOR) + m_radInitYAngle;
+		if(bClearXZ)
+			m_radCurrXZAngle = m_radInitXZAngle;
 	}
 
 	void MousePole::ProcessSpinAxis(int iXDiff, int iYDiff)
 		m_bIsDragging = false;
 	}
 
-	void MousePole::MoveCloser( bool bLargeStep /*= true*/ )
+	void MousePole::MoveCloser( bool bLargeStep )
 	{
 		if(bLargeStep)
 			m_radius.fCurrRadius -= m_radius.fLargeDelta;
 			//Ignore all other button presses when dragging.
 			if(!m_bIsDragging)
 			{
-				switch(button)
+				if(button == m_glutActionButton)
 				{
-				case GLUT_LEFT_BUTTON:
 					if(glutGetModifiers() & GLUT_ACTIVE_CTRL)
-						this->BeginDragRotate(position, MousePole::RM_XZ_AXIS_ROTATE);
+						this->BeginDragRotate(position, MousePole::RM_BIAXIAL_ROTATE);