Commits

Jason McKesson committed d295d5e

Positional lights in per-vertex lighting code works.
Interpolation also has working code.

  • Participants
  • Parent commits 5976f71

Comments (0)

Files changed (25)

File Documents/Outline.xml

             </itemizedlist>
         </section>
         <section>
-            <title>Of Metal and Plastic</title>
-            <para>This tutorial involves creating a single mesh that has multiple lighting models:
-                one reflective and one very diffuse. There should be an animated light or two that
+            <title>Shininess</title>
+            <para>This tutorial introduces ways to make an object look shiny. Specifically specular
+                lighting and the Phong lighting model. There should be an animated light or two that
                 shows this off.</para>
             <para>Concepts:</para>
             <itemizedlist>
         <section>
             <title>Climbing the Mountain</title>
             <para>This tutorial uses a height map and adjust vertex positions and normals to match
-                it. The height map is a texture. No lighting yet.</para>
+                it. The height map is a texture.</para>
             <para>Concepts:</para>
             <itemizedlist>
                 <listitem>
                 </listitem>
             </itemizedlist>
         </section>
+        <section>
+            <title>Spot Lights</title>
+            <para>Use a projected texture to imitate a spot light.</para>
+            <para>Concepts:</para>
+            <itemizedlist>
+                <listitem>
+                    <para>Projective texturing. The concept of it and the GLSL language
+                        support.</para>
+                </listitem>
+                <listitem>
+                    <para>Textures as light sources. The texel values are light intensity values,
+                        not diffuse colors.</para>
+                </listitem>
+                <listitem>
+                    <para>Using a global texture over a scene.</para>
+                </listitem>
+            </itemizedlist>
+        </section>
     </section>
     <section>
         <title>Framebuffers</title>
                         done before reduction to the integer colorspace.</para>
                 </listitem>
                 <listitem>
-                    <para>Introduce R11_G11_B10 as an optimization.</para>
+                    <para>Introduce R11F_G11F_B10F as an optimization.</para>
                 </listitem>
             </itemizedlist>
         </section>
         <title>Functionality that needs tutorials</title>
         <glosslist>
             <glossentry>
+                <glossterm>sRGB Colorspace</glossterm>
+                <glossdef>
+                    <para/>
+                </glossdef>
+            </glossentry>
+            <glossentry>
                 <glossterm>Radial fog</glossterm>
                 <glossdef>
                     <para/>

File Tut 09 Plane Lights/Perspective Interpolation.cpp

+#include <string>
+#include <vector>
+#include <stack>
+#include <math.h>
+#include <glloader/gl_3_2_comp.h>
+#include <GL/freeglut.h>
+#include "../framework/framework.h"
+#include "../framework/Mesh.h"
+#include "../framework/MatrixStack.h"
+#include "../framework/MousePole.h"
+#include <glm/glm.hpp>
+#include <glm/gtc/type_ptr.hpp>
+
+#define ARRAY_COUNT( array ) (sizeof( array ) / (sizeof( array[0] ) * (sizeof( array ) != sizeof(void*) || sizeof( array[0] ) <= sizeof(void*))))
+
+struct ProgramData
+{
+	GLuint theProgram;
+
+	GLuint cameraToClipMatrixUnif;
+};
+
+float g_fzNear = 1.0f;
+float g_fzFar = 1000.0f;
+
+ProgramData g_SmoothInterp;
+ProgramData g_LinearInterp;
+
+ProgramData LoadProgram(const std::string &strVertexShader, const std::string &strFragmentShader)
+{
+	std::vector<GLuint> shaderList;
+
+	shaderList.push_back(Framework::LoadShader(GL_VERTEX_SHADER, strVertexShader));
+	shaderList.push_back(Framework::LoadShader(GL_FRAGMENT_SHADER, strFragmentShader));
+
+	ProgramData data;
+	data.theProgram = Framework::CreateProgram(shaderList);
+	data.cameraToClipMatrixUnif = glGetUniformLocation(data.theProgram, "cameraToClipMatrix");
+
+	return data;
+}
+
+void InitializePrograms()
+{
+	g_SmoothInterp = LoadProgram("SmoothVertexColors.vert", "SmoothVertexColors.frag");
+	g_LinearInterp = LoadProgram("NoCorrectVertexColors.vert", "NoCorrectVertexColors.frag");
+
+	Framework::MatrixStack persMatrix;
+	persMatrix.Perspective(60.0f, 1.0f, g_fzNear, g_fzFar);
+
+	glUseProgram(g_SmoothInterp.theProgram);
+	glUniformMatrix4fv(g_SmoothInterp.cameraToClipMatrixUnif, 1, GL_FALSE,
+		glm::value_ptr(persMatrix.Top()));
+	glUseProgram(g_LinearInterp.theProgram);
+	glUniformMatrix4fv(g_LinearInterp.cameraToClipMatrixUnif, 1, GL_FALSE,
+		glm::value_ptr(persMatrix.Top()));
+	glUseProgram(0);
+}
+
+Framework::Mesh *g_pRealHallway = NULL;
+Framework::Mesh *g_pFauxHallway = NULL;
+
+//Called after the window and OpenGL are initialized. Called exactly once, before the main loop.
+void init()
+{
+	InitializePrograms();
+
+	try
+	{
+		g_pRealHallway = new Framework::Mesh("RealHallway.xml");
+		g_pFauxHallway = new Framework::Mesh("FauxHallway.xml");
+	}
+	catch(std::exception &except)
+	{
+		printf(except.what());
+	}
+}
+
+static bool g_bUseFakeHallway = false;
+static bool g_bUseSmoothInterpolation = true;
+
+//Called to update the display.
+//You should call glutSwapBuffers after all of your rendering to display what you rendered.
+//If you need continuous updates of the screen, call glutPostRedisplay() at the end of the function.
+
+void display()
+{
+	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+	glClearDepth(1.0f);
+	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+	if(g_pRealHallway && g_pFauxHallway)
+	{
+		if(g_bUseSmoothInterpolation)
+			glUseProgram(g_SmoothInterp.theProgram);
+		else
+			glUseProgram(g_LinearInterp.theProgram);
+
+		if(g_bUseFakeHallway)
+			g_pFauxHallway->Render();
+		else
+			g_pRealHallway->Render();
+
+		glUseProgram(0);
+	}
+
+	glutSwapBuffers();
+}
+
+//Called whenever the window is resized. The new window size is given, in pixels.
+//This is an opportunity to call glViewport or glScissor to keep up with the change in size.
+void reshape (int w, int h)
+{
+	glViewport(0, 0, (GLsizei) w, (GLsizei) h);
+	glutPostRedisplay();
+}
+
+//Called whenever a key on the keyboard was pressed.
+//The key is given by the ''key'' parameter, which is in ASCII.
+//It's often a good idea to have the escape key (ASCII value 27) call glutLeaveMainLoop() to 
+//exit the program.
+void keyboard(unsigned char key, int x, int y)
+{
+	switch (key)
+	{
+	case 27:
+		delete g_pRealHallway;
+		delete g_pFauxHallway;
+		glutLeaveMainLoop();
+		break;
+
+	case 's':
+		g_bUseFakeHallway = !g_bUseFakeHallway;
+		if(g_bUseFakeHallway)
+			printf("Fake Hallway.\n");
+		else
+			printf("Real Hallway.\n");
+		break;
+
+	case 'w':
+		g_bUseSmoothInterpolation = !g_bUseSmoothInterpolation;
+		if(g_bUseSmoothInterpolation)
+			printf("Perspective correct interpolation.\n");
+		else
+			printf("Just linear interpolation.\n");
+		break;
+
+	case 32:
+		//Reload.
+		delete g_pRealHallway;
+		delete g_pFauxHallway;
+		g_pRealHallway = new Framework::Mesh("RealHallway.xml");
+		g_pFauxHallway = new Framework::Mesh("FauxHallway.xml");
+		break;
+	}
+
+	glutPostRedisplay();
+}
+
+

File Tut 09 Plane Lights/Vertex Directional Lighting.cpp

+#include <string>
+#include <vector>
+#include <stack>
+#include <math.h>
+#include <glloader/gl_3_2_comp.h>
+#include <GL/freeglut.h>
+#include "../framework/framework.h"
+#include "../framework/Mesh.h"
+#include "../framework/MatrixStack.h"
+#include "../framework/MousePole.h"
+#include <glm/glm.hpp>
+#include <glm/gtc/type_ptr.hpp>
+
+#define ARRAY_COUNT( array ) (sizeof( array ) / (sizeof( array[0] ) * (sizeof( array ) != sizeof(void*) || sizeof( array[0] ) <= sizeof(void*))))
+
+struct ProgramData
+{
+	GLuint theProgram;
+
+	GLuint lightPosUnif;
+	GLuint lightIntensityUnif;
+	GLuint ambientIntensityUnif;
+
+	GLuint cameraToClipMatrixUnif;
+	GLuint modelToCameraMatrixUnif;
+	GLuint normalModelToCameraMatrixUnif;
+};
+
+struct UnlitProgData
+{
+	GLuint theProgram;
+
+	GLuint objectColorUnif;
+	GLuint cameraToClipMatrixUnif;
+	GLuint modelToCameraMatrixUnif;
+};
+
+float g_fzNear = 1.0f;
+float g_fzFar = 1000.0f;
+
+ProgramData g_WhiteAmbDiffuseColor;
+ProgramData g_VertexAmbDiffuseColor;
+
+UnlitProgData g_Unlit;
+
+UnlitProgData LoadUnlitProgram(const std::string &strVertexShader, const std::string &strFragmentShader)
+{
+	std::vector<GLuint> shaderList;
+
+	shaderList.push_back(Framework::LoadShader(GL_VERTEX_SHADER, strVertexShader));
+	shaderList.push_back(Framework::LoadShader(GL_FRAGMENT_SHADER, strFragmentShader));
+
+	UnlitProgData data;
+	data.theProgram = Framework::CreateProgram(shaderList);
+	data.modelToCameraMatrixUnif = glGetUniformLocation(data.theProgram, "modelToCameraMatrix");
+	data.cameraToClipMatrixUnif = glGetUniformLocation(data.theProgram, "cameraToClipMatrix");
+	data.objectColorUnif = glGetUniformLocation(data.theProgram, "objectColor");
+
+	return data;
+}
+
+ProgramData LoadLitProgram(const std::string &strVertexShader, const std::string &strFragmentShader)
+{
+	std::vector<GLuint> shaderList;
+
+	shaderList.push_back(Framework::LoadShader(GL_VERTEX_SHADER, strVertexShader));
+	shaderList.push_back(Framework::LoadShader(GL_FRAGMENT_SHADER, strFragmentShader));
+
+	ProgramData data;
+	data.theProgram = Framework::CreateProgram(shaderList);
+	data.modelToCameraMatrixUnif = glGetUniformLocation(data.theProgram, "modelToCameraMatrix");
+	data.cameraToClipMatrixUnif = glGetUniformLocation(data.theProgram, "cameraToClipMatrix");
+	data.normalModelToCameraMatrixUnif = glGetUniformLocation(data.theProgram, "normalModelToCameraMatrix");
+	data.lightPosUnif = glGetUniformLocation(data.theProgram, "lightPos");
+	data.lightIntensityUnif = glGetUniformLocation(data.theProgram, "lightIntensity");
+	data.ambientIntensityUnif = glGetUniformLocation(data.theProgram, "ambientIntensity");
+
+	return data;
+}
+
+void InitializePrograms()
+{
+	g_WhiteAmbDiffuseColor = LoadLitProgram("PosVertexLighting_PN.vert", "ColorPassthrough.frag");
+	g_VertexAmbDiffuseColor = LoadLitProgram("PosVertexLighting_PCN.vert", "ColorPassthrough.frag");
+	g_Unlit = LoadUnlitProgram("PosTransform.vert", "UniformColor.frag");
+}
+
+Framework::Mesh *g_pCylinderMesh = NULL;
+Framework::Mesh *g_pPlaneMesh = NULL;
+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);
+
+namespace
+{
+	void MouseMotion(int x, int y)
+	{
+		g_mousePole.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));
+		glutPostRedisplay();
+	}
+
+	void MouseWheel(int wheel, int direction, int x, int y)
+	{
+		g_mousePole.GLUTMouseWheel(direction, glm::ivec2(x, y));
+		glutPostRedisplay();
+	}
+}
+
+//Called after the window and OpenGL are initialized. Called exactly once, before the main loop.
+void init()
+{
+	InitializePrograms();
+
+	try
+	{
+		g_pCylinderMesh = new Framework::Mesh("UnitCylinder.xml");
+		g_pPlaneMesh = new Framework::Mesh("LargePlane.xml");
+		g_pCubeMesh = new Framework::Mesh("UnitCube.xml");
+	}
+	catch(std::exception &except)
+	{
+		printf(except.what());
+	}
+
+ 	glutMouseFunc(MouseButton);
+ 	glutMotionFunc(MouseMotion);
+	glutMouseWheelFunc(MouseWheel);
+
+	glEnable(GL_CULL_FACE);
+	glCullFace(GL_BACK);
+	glFrontFace(GL_CW);
+
+	glEnable(GL_DEPTH_TEST);
+	glDepthMask(GL_TRUE);
+	glDepthFunc(GL_LEQUAL);
+	glDepthRange(0.0f, 1.0f);
+	glEnable(GL_DEPTH_CLAMP);
+}
+
+static float g_fLightHeight = 1.5f;
+static float g_fLightRadius = 1.0f;
+
+glm::vec4 CalcLightPosition()
+{
+	const float fLoopDuration = 5.0f;
+	const float fScale = 3.14159f * 2.0f / fLoopDuration;
+
+	float fElapsedTime = glutGet(GLUT_ELAPSED_TIME) / 1000.0f;
+
+	float fCurrTimeThroughLoop = fmodf(fElapsedTime, fLoopDuration);
+
+	glm::vec4 ret(0.0f, g_fLightHeight, 0.0f, 1.0f);
+
+	ret.x = cosf(fCurrTimeThroughLoop * fScale) * g_fLightRadius;
+	ret.z = sinf(fCurrTimeThroughLoop * fScale) * g_fLightRadius;
+
+	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;
+
+//Called to update the display.
+//You should call glutSwapBuffers after all of your rendering to display what you rendered.
+//If you need continuous updates of the screen, call glutPostRedisplay() at the end of the function.
+
+void display()
+{
+	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+	glClearDepth(1.0f);
+	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+	if(g_pPlaneMesh && g_pCylinderMesh)
+	{
+		Framework::MatrixStack modelMatrix;
+		modelMatrix.SetMatrix(g_mousePole.CalcMatrix());
+
+		const glm::vec4 &worldLightPos = CalcLightPosition();
+
+		glm::vec4 lightPosCameraSpace = modelMatrix.Top() * worldLightPos;
+
+		glUseProgram(g_WhiteAmbDiffuseColor.theProgram);
+		glUniform4f(g_WhiteAmbDiffuseColor.lightIntensityUnif, 0.8f, 0.8f, 0.8f, 1.0f);
+		glUniform4f(g_WhiteAmbDiffuseColor.ambientIntensityUnif, 0.2f, 0.2f, 0.2f, 1.0f);
+		glUseProgram(g_VertexAmbDiffuseColor.theProgram);
+		glUniform4f(g_VertexAmbDiffuseColor.lightIntensityUnif, 0.8f, 0.8f, 0.8f, 1.0f);
+		glUniform4f(g_VertexAmbDiffuseColor.ambientIntensityUnif, 0.2f, 0.2f, 0.2f, 1.0f);
+
+		glUseProgram(g_WhiteAmbDiffuseColor.theProgram);
+		glUniform3fv(g_WhiteAmbDiffuseColor.lightPosUnif, 1, glm::value_ptr(lightPosCameraSpace));
+		glUseProgram(g_VertexAmbDiffuseColor.theProgram);
+		glUniform3fv(g_VertexAmbDiffuseColor.lightPosUnif, 1, glm::value_ptr(lightPosCameraSpace));
+		glUseProgram(0);
+
+		{
+			Framework::MatrixStackPusher push(modelMatrix);
+
+			//Render the ground plane.
+			{
+				Framework::MatrixStackPusher push(modelMatrix);
+
+				glUseProgram(g_WhiteAmbDiffuseColor.theProgram);
+				glUniformMatrix4fv(g_WhiteAmbDiffuseColor.modelToCameraMatrixUnif, 1, GL_FALSE,
+					glm::value_ptr(modelMatrix.Top()));
+				glm::mat3 normMatrix(modelMatrix.Top());
+				glUniformMatrix3fv(g_WhiteAmbDiffuseColor.normalModelToCameraMatrixUnif, 1, GL_FALSE,
+					glm::value_ptr(normMatrix));
+				g_pPlaneMesh->Render();
+				glUseProgram(0);
+			}
+
+			//Render the Cylinder
+			{
+				Framework::MatrixStackPusher push(modelMatrix);
+
+				modelMatrix.Translate(0.0f, 0.5f, 0.0f);
+
+				modelMatrix.RotateX(g_CylPitch);
+				modelMatrix.RotateY(g_CylYaw);
+				modelMatrix.RotateZ(g_CylRoll);
+
+				if(g_bDrawColoredCyl)
+				{
+					glUseProgram(g_VertexAmbDiffuseColor.theProgram);
+					glUniformMatrix4fv(g_VertexAmbDiffuseColor.modelToCameraMatrixUnif, 1, GL_FALSE,
+						glm::value_ptr(modelMatrix.Top()));
+					glm::mat3 normMatrix(modelMatrix.Top());
+					glUniformMatrix3fv(g_VertexAmbDiffuseColor.normalModelToCameraMatrixUnif, 1, GL_FALSE,
+						glm::value_ptr(normMatrix));
+					g_pCylinderMesh->Render("tint");
+				}
+				else
+				{
+					glUseProgram(g_WhiteAmbDiffuseColor.theProgram);
+					glUniformMatrix4fv(g_WhiteAmbDiffuseColor.modelToCameraMatrixUnif, 1, GL_FALSE,
+						glm::value_ptr(modelMatrix.Top()));
+					glm::mat3 normMatrix(modelMatrix.Top());
+					glUniformMatrix3fv(g_WhiteAmbDiffuseColor.normalModelToCameraMatrixUnif, 1, GL_FALSE,
+						glm::value_ptr(normMatrix));
+					g_pCylinderMesh->Render("flat");
+				}
+				glUseProgram(0);
+			}
+
+			//Render the light
+			if(g_bDrawLight)
+			{
+				Framework::MatrixStackPusher push(modelMatrix);
+
+				modelMatrix.Translate(glm::vec3(worldLightPos));
+				modelMatrix.Scale(0.1f, 0.1f, 0.1f);
+
+				glUseProgram(g_Unlit.theProgram);
+				glUniformMatrix4fv(g_Unlit.modelToCameraMatrixUnif, 1, GL_FALSE,
+					glm::value_ptr(modelMatrix.Top()));
+				glUniform4f(g_Unlit.objectColorUnif, 0.8078f, 0.8706f, 0.9922f, 1.0f);
+				g_pCubeMesh->Render("flat");
+			}
+		}
+	}
+
+	glutPostRedisplay();
+	glutSwapBuffers();
+}
+
+//Called whenever the window is resized. The new window size is given, in pixels.
+//This is an opportunity to call glViewport or glScissor to keep up with the change in size.
+void reshape (int w, int h)
+{
+	Framework::MatrixStack persMatrix;
+	persMatrix.Perspective(45.0f, (h / (float)w), g_fzNear, g_fzFar);
+
+	glUseProgram(g_WhiteAmbDiffuseColor.theProgram);
+	glUniformMatrix4fv(g_WhiteAmbDiffuseColor.cameraToClipMatrixUnif, 1, GL_FALSE,
+		glm::value_ptr(persMatrix.Top()));
+	glUseProgram(g_VertexAmbDiffuseColor.theProgram);
+	glUniformMatrix4fv(g_VertexAmbDiffuseColor.cameraToClipMatrixUnif, 1, GL_FALSE,
+		glm::value_ptr(persMatrix.Top()));
+	glUseProgram(g_Unlit.theProgram);
+	glUniformMatrix4fv(g_Unlit.cameraToClipMatrixUnif, 1, GL_FALSE,
+		glm::value_ptr(persMatrix.Top()));
+	glUseProgram(0);
+
+	glViewport(0, 0, (GLsizei) w, (GLsizei) h);
+	glutPostRedisplay();
+}
+
+//Called whenever a key on the keyboard was pressed.
+//The key is given by the ''key'' parameter, which is in ASCII.
+//It's often a good idea to have the escape key (ASCII value 27) call glutLeaveMainLoop() to 
+//exit the program.
+void keyboard(unsigned char key, int x, int y)
+{
+	switch (key)
+	{
+	case 27:
+		delete g_pPlaneMesh;
+		delete g_pCylinderMesh;
+		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;
+	case 'k': g_fLightHeight -= 0.2f; break;
+	case 'l': g_fLightRadius += 0.2f; break;
+	case 'j': g_fLightRadius -= 0.2f; break;
+
+	case 'I': g_fLightHeight += 0.05f; break;
+	case 'K': g_fLightHeight -= 0.05f; break;
+	case 'L': g_fLightRadius += 0.05f; break;
+	case 'J': g_fLightRadius -= 0.05f; break;
+
+	case 'y': g_bDrawLight = !g_bDrawLight; break;
+	}
+
+	if(g_fLightRadius < 0.2f)
+		g_fLightRadius = 0.2f;
+
+	glutPostRedisplay();
+}
+
+

File Tut 09 Plane Lights/data/ColorPassthrough.frag

+#version 330
+
+smooth in vec4 interpColor;
+
+out vec4 outputColor;
+
+void main()
+{
+	outputColor = interpColor;
+}

File Tut 09 Plane Lights/data/FauxHallway.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<?oxygen RNGSchema="../../Documents/meshFormat.rnc" type="compact"?>
+
+<mesh xmlns="http://www.arcsynthesis.com/gltut/mesh" >
+	<attribute index="0" type="float" size="3" > 
+        -1 1 -2
+        -1 -1 -2
+        1 -1 -2
+        1 1 -2
+        -0.4 0.4 -2
+        -0.4 -0.4 -2
+        0.4 -0.4 -2
+        0.4 0.4 -2
+        -0.4 0.4 -2
+        -0.4 -0.4 -2
+        0.4 -0.4 -2
+        0.4 0.4 -2
+        -0.25 0.25 -2
+        -0.25 -0.25 -2
+        0.25 -0.25 -2
+        0.25 0.25 -2</attribute>
+	<attribute index="1" type="float" size="4" > 
+        1 0 0 1
+        1 0 0 1
+        1 0 0 1
+        1 0 0 1
+        0.5 0.5 0 1
+        0.5 0.5 0 1
+        0.5 0.5 0 1
+        0.5 0.5 0 1
+        0.5 0.5 0 1
+        0.5 0.5 0 1
+        0.5 0.5 0 1
+        0.5 0.5 0 1
+        0 1 0 1
+        0 1 0 1
+        0 1 0 1
+        0 1 0 1</attribute>
+	<indices cmd="triangles" type="ushort" >0 1 4 1 5 4 1 2 6 1 6 5 2 6 3 3 6 7 8 9 12 9 13 12 9 10 14 9 14 13 10 14 11 11 14 15</indices>
+</mesh>

File Tut 09 Plane Lights/data/GenCube.lua

+require "XmlWriter"
+require "vmath"
+
+local function GenStringFromArray(theArray, bAsInt)
+	local array = {" "}
+	for i, vector in ipairs(theArray) do
+		local elements = vector;
+		if(bAsInt) then
+			elements = {};
+			for i, value in ipairs(vector) do
+				elements[#elements + 1] = string.format("%i", value);
+			end
+		end
+		
+		array[#array + 1] = "        " .. table.concat(vector, " ");
+	end
+	
+	return table.concat(array, "\n");
+end
+
+local positions =
+{
+	--Front
+	vmath.vec3( 0.5,  0.5,  0.5),
+	vmath.vec3( 0.5, -0.5,  0.5),
+	vmath.vec3(-0.5, -0.5,  0.5),
+	vmath.vec3(-0.5,  0.5,  0.5),
+
+	--Top
+	vmath.vec3( 0.5,  0.5,  0.5),
+	vmath.vec3(-0.5,  0.5,  0.5),
+	vmath.vec3(-0.5,  0.5, -0.5),
+	vmath.vec3( 0.5,  0.5, -0.5),
+
+	--Left
+	vmath.vec3( 0.5,  0.5,  0.5),
+	vmath.vec3( 0.5,  0.5, -0.5),
+	vmath.vec3( 0.5, -0.5, -0.5),
+	vmath.vec3( 0.5, -0.5,  0.5),
+
+	--Back
+	vmath.vec3( 0.5,  0.5, -0.5),
+	vmath.vec3(-0.5,  0.5, -0.5),
+	vmath.vec3(-0.5, -0.5, -0.5),
+	vmath.vec3( 0.5, -0.5, -0.5),
+
+	--Bottom
+	vmath.vec3( 0.5, -0.5,  0.5),
+	vmath.vec3( 0.5, -0.5, -0.5),
+	vmath.vec3(-0.5, -0.5, -0.5),
+	vmath.vec3(-0.5, -0.5,  0.5),
+
+	--Right
+	vmath.vec3(-0.5,  0.5,  0.5),
+	vmath.vec3(-0.5, -0.5,  0.5),
+	vmath.vec3(-0.5, -0.5, -0.5),
+	vmath.vec3(-0.5,  0.5, -0.5),
+};
+
+local tints =
+{
+	vmath.vec4(1.0, 1.0, 1.0, 1.0),
+	vmath.vec4(1.0, 1.0, 1.0, 1.0),
+	vmath.vec4(1.0, 1.0, 1.0, 1.0),
+	vmath.vec4(1.0, 1.0, 1.0, 1.0),
+
+	vmath.vec4(0.75, 0.75, 0.75, 1.0),
+	vmath.vec4(0.75, 0.75, 0.75, 1.0),
+	vmath.vec4(0.75, 0.75, 0.75, 1.0),
+	vmath.vec4(0.75, 0.75, 0.75, 1.0),
+
+	vmath.vec4(0.5, 0.5, 0.5, 1.0),
+	vmath.vec4(0.5, 0.5, 0.5, 1.0),
+	vmath.vec4(0.5, 0.5, 0.5, 1.0),
+	vmath.vec4(0.5, 0.5, 0.5, 1.0),
+
+	vmath.vec4(1.0, 1.0, 1.0, 1.0),
+	vmath.vec4(1.0, 1.0, 1.0, 1.0),
+	vmath.vec4(1.0, 1.0, 1.0, 1.0),
+	vmath.vec4(1.0, 1.0, 1.0, 1.0),
+
+	vmath.vec4(0.75, 0.75, 0.75, 1.0),
+	vmath.vec4(0.75, 0.75, 0.75, 1.0),
+	vmath.vec4(0.75, 0.75, 0.75, 1.0),
+	vmath.vec4(0.75, 0.75, 0.75, 1.0),
+
+	vmath.vec4(0.5, 0.5, 0.5, 1.0),
+	vmath.vec4(0.5, 0.5, 0.5, 1.0),
+	vmath.vec4(0.5, 0.5, 0.5, 1.0),
+	vmath.vec4(0.5, 0.5, 0.5, 1.0),
+}
+
+local colors =
+{
+	vmath.vec4(0.0, 1.0, 0.0, 1.0),
+	vmath.vec4(0.0, 1.0, 0.0, 1.0),
+	vmath.vec4(0.0, 1.0, 0.0, 1.0),
+	vmath.vec4(0.0, 1.0, 0.0, 1.0),
+
+	vmath.vec4(0.0, 0.0, 1.0, 1.0),
+	vmath.vec4(0.0, 0.0, 1.0, 1.0),
+	vmath.vec4(0.0, 0.0, 1.0, 1.0),
+	vmath.vec4(0.0, 0.0, 1.0, 1.0),
+
+	vmath.vec4(1.0, 0.0, 0.0, 1.0),
+	vmath.vec4(1.0, 0.0, 0.0, 1.0),
+	vmath.vec4(1.0, 0.0, 0.0, 1.0),
+	vmath.vec4(1.0, 0.0, 0.0, 1.0),
+
+	vmath.vec4(1.0, 1.0, 0.0, 1.0),
+	vmath.vec4(1.0, 1.0, 0.0, 1.0),
+	vmath.vec4(1.0, 1.0, 0.0, 1.0),
+	vmath.vec4(1.0, 1.0, 0.0, 1.0),
+
+	vmath.vec4(0.0, 1.0, 1.0, 1.0),
+	vmath.vec4(0.0, 1.0, 1.0, 1.0),
+	vmath.vec4(0.0, 1.0, 1.0, 1.0),
+	vmath.vec4(0.0, 1.0, 1.0, 1.0),
+
+	vmath.vec4(1.0, 0.0, 1.0, 1.0),
+	vmath.vec4(1.0, 0.0, 1.0, 1.0),
+	vmath.vec4(1.0, 0.0, 1.0, 1.0),
+	vmath.vec4(1.0, 0.0, 1.0, 1.0),
+}
+
+local indices =
+{
+	vmath.vec3(0, 1, 2),
+	vmath.vec3(2, 3, 0),
+
+	vmath.vec3(4, 5, 6),
+	vmath.vec3(6, 7, 4),
+
+	vmath.vec3(8, 9, 10),
+	vmath.vec3(10, 11, 8),
+
+	vmath.vec3(12, 13, 14),
+	vmath.vec3(14, 15, 12),
+
+	vmath.vec3(16, 17, 18),
+	vmath.vec3(18, 19, 16),
+
+	vmath.vec3(20, 21, 22),
+	vmath.vec3(22, 23, 20),
+};
+
+do
+	local writer = XmlWriter.XmlWriter("UnitCube.xml");
+	writer:AddPI("oxygen", [[RNGSchema="../../Documents/meshFormat.rnc" type="compact"]]);
+	writer:PushElement("mesh", "http://www.arcsynthesis.com/gltut/mesh");
+		writer:PushElement("attribute");
+			writer:AddAttribute("index", "0");
+			writer:AddAttribute("type", "float");
+			writer:AddAttribute("size", "3");
+			writer:AddText(GenStringFromArray(positions));
+		writer:PopElement();
+		writer:PushElement("attribute");
+			writer:AddAttribute("index", "1");
+			writer:AddAttribute("type", "float");
+			writer:AddAttribute("size", "4");
+			writer:AddText(GenStringFromArray(colors));
+		writer:PopElement();
+		writer:PushElement("vao");
+			writer:AddAttribute("name", "color");
+			writer:PushElement("source"); writer:AddAttribute("attrib", "0"); writer:PopElement();
+			writer:PushElement("source"); writer:AddAttribute("attrib", "1"); writer:PopElement();
+		writer:PopElement();
+		writer:PushElement("vao");
+			writer:AddAttribute("name", "flat");
+			writer:PushElement("source"); writer:AddAttribute("attrib", "0"); writer:PopElement();
+		writer:PopElement();
+		writer:PushElement("indices");
+			writer:AddAttribute("cmd", "triangles");
+			writer:AddAttribute("type", "ushort");
+			writer:AddText(GenStringFromArray(indices, true));
+		writer:PopElement();
+	writer:PopElement();
+	writer:Close();
+end

File Tut 09 Plane Lights/data/GenCylinder.lua

+require "XmlWriter"
+require "vmath"
+
+local function GenStringFromArray(theArray)
+	local array = {" "}
+	for i, vector in ipairs(theArray) do
+		array[#array + 1] = "        " .. table.concat(vector, " ");
+	end
+	
+	return table.concat(array, "\n");
+end
+
+local positions = {};
+local colors = {};
+local normals = {};
+local topFan = {};
+local botFan = {};
+local cylStrip = {};
+
+local iSegCount, iColorRepeatCount = ...;
+iSegCount = iSegCount or 30;
+iColorRepeatCount = iColorRepeatCount or 3;
+
+local iAngle = 3.14159 * 2.0 / iSegCount;
+local iColorCycleAngle = 3.14159 * 2.0 / iColorRepeatCount;
+local highColor = vmath.vec4(0.9, 0.5, 0.5, 1.0);
+local lowColor = vmath.vec4(0.5, 0.1, 0.1, 1.0)
+
+--Compute caps
+positions[#positions + 1] = vmath.vec3(0.0, 0.5, 0.0);
+colors[#colors + 1] = vmath.vec4(1.0, 1.0, 1.0, 1.0);
+normals[#normals + 1] = vmath.vec3(0.0, 1.0, 0.0);
+topFan[#topFan + 1] = 0;
+botFan[#botFan + 1] = (iSegCount * 2) + 1;
+
+local firstSideIx = #positions;
+
+for iSeg = 0, (iSegCount - 1), 1 do
+	local iCurrAngle = iSeg * iAngle;
+
+	positions[#positions + 1] =
+		vmath.vec3(0.5 * math.cos(iCurrAngle), 0.5, 0.5 * math.sin(iCurrAngle));
+	positions[#positions + 1] =
+		vmath.vec3(0.5 * math.cos(iCurrAngle), -0.5, 0.5 * math.sin(iCurrAngle));
+
+	normals[#normals + 1] = vmath.vec3(0.0, 1.0, 0.0);
+	normals[#normals + 1] = vmath.vec3(0.0, -1.0, 0.0);
+		
+	local clrDist = math.mod(iCurrAngle, iColorCycleAngle) / iColorCycleAngle;
+	if(clrDist > 0.5) then
+		local interp = (clrDist - 0.5) * 2;
+		colors[#colors + 1] = (interp * highColor) +
+			((1 - interp) * lowColor);
+	else
+		local interp = clrDist * 2;
+		colors[#colors + 1] = (interp * lowColor) +
+			((1 - interp) * highColor);
+	end
+	
+	colors[#colors + 1] = colors[#colors];
+
+	topFan[#topFan + 1] = 1 + (iSeg * 2);
+	botFan[#botFan + 1] = 1 + (((iSegCount - iSeg) * 2) - 1);
+end
+
+topFan[#topFan + 1] = topFan[2];
+botFan[#botFan + 1] = botFan[2];
+
+positions[#positions + 1] = vmath.vec3(0.0, -0.5, 0.0);
+colors[#colors + 1] = vmath.vec4(1.0, 1.0, 1.0, 1.0);
+normals[#normals + 1] = vmath.vec3(0.0, -1.0, 0.0);
+
+--Compute sides.
+for iSeg = 0, (iSegCount - 1), 1 do
+	local iCurrAngle = iSeg * iAngle;
+
+	positions[#positions + 1] =
+		vmath.vec3(0.5 * math.cos(iCurrAngle), 0.5, 0.5 * math.sin(iCurrAngle));
+	positions[#positions + 1] =
+		vmath.vec3(0.5 * math.cos(iCurrAngle), -0.5, 0.5 * math.sin(iCurrAngle));
+
+	normals[#normals + 1] = vmath.vec3(math.cos(iCurrAngle), 0, math.sin(iCurrAngle));
+	normals[#normals + 1] = normals[#normals];
+		
+	local clrDist = math.mod(iCurrAngle, iColorCycleAngle) / iColorCycleAngle;
+	if(clrDist > 0.5) then
+		local interp = (clrDist - 0.5) * 2;
+		colors[#colors + 1] = (interp * highColor) +
+			((1 - interp) * lowColor);
+	else
+		local interp = clrDist * 2;
+		colors[#colors + 1] = (interp * lowColor) +
+			((1 - interp) * highColor);
+	end
+	
+	colors[#colors + 1] = colors[#colors];
+
+	cylStrip[#cylStrip + 1] = #positions - 2;
+	cylStrip[#cylStrip + 1] = #positions - 1;
+end
+
+cylStrip[#cylStrip + 1] = cylStrip[1];
+cylStrip[#cylStrip + 1] = cylStrip[2];
+
+do
+	local writer = XmlWriter.XmlWriter("UnitCylinder.xml");
+	writer:AddPI("oxygen", [[RNGSchema="../../Documents/meshFormat.rnc" type="compact"]]);
+	writer:PushElement("mesh", "http://www.arcsynthesis.com/gltut/mesh");
+		writer:PushElement("attribute");
+			writer:AddAttribute("index", "0");
+			writer:AddAttribute("type", "float");
+			writer:AddAttribute("size", "3");
+			writer:AddText(GenStringFromArray(positions));
+		writer:PopElement();
+		writer:PushElement("attribute");
+			writer:AddAttribute("index", "1");
+			writer:AddAttribute("type", "float");
+			writer:AddAttribute("size", "4");
+			writer:AddText(GenStringFromArray(colors));
+		writer:PopElement();
+		writer:PushElement("attribute");
+			writer:AddAttribute("index", "2");
+			writer:AddAttribute("type", "float");
+			writer:AddAttribute("size", "3");
+			writer:AddText(GenStringFromArray(normals));
+		writer:PopElement();
+		writer:PushElement("vao");
+			writer:AddAttribute("name", "tint");
+			writer:PushElement("source"); writer:AddAttribute("attrib", "0"); writer:PopElement();
+			writer:PushElement("source"); writer:AddAttribute("attrib", "1"); writer:PopElement();
+			writer:PushElement("source"); writer:AddAttribute("attrib", "2"); writer:PopElement();
+		writer:PopElement();
+		writer:PushElement("vao");
+			writer:AddAttribute("name", "flat");
+			writer:PushElement("source"); writer:AddAttribute("attrib", "0"); writer:PopElement();
+			writer:PushElement("source"); writer:AddAttribute("attrib", "2"); writer:PopElement();
+		writer:PopElement();
+		writer:PushElement("vao");
+			writer:AddAttribute("name", "tint-unlit");
+			writer:PushElement("source"); writer:AddAttribute("attrib", "0"); writer:PopElement();
+			writer:PushElement("source"); writer:AddAttribute("attrib", "1"); writer:PopElement();
+		writer:PopElement();
+		writer:PushElement("indices");
+			writer:AddAttribute("cmd", "tri-fan");
+			writer:AddAttribute("type", "ushort");
+			writer:AddText(table.concat(topFan, " "));
+		writer:PopElement();
+		writer:PushElement("indices");
+			writer:AddAttribute("cmd", "tri-fan");
+			writer:AddAttribute("type", "ushort");
+			writer:AddText(table.concat(botFan, " "));
+		writer:PopElement();
+		writer:PushElement("indices");
+			writer:AddAttribute("cmd", "tri-strip");
+			writer:AddAttribute("type", "ushort");
+			writer:AddText(table.concat(cylStrip, " "));
+		writer:PopElement();
+	writer:PopElement();
+	writer:Close();
+end

File Tut 09 Plane Lights/data/GenHallways.lua

+require "XmlWriter"
+require "vmath"
+
+local function GenStringFromArray(theArray, bAsInt)
+	local array = {" "}
+	for i, vector in ipairs(theArray) do
+		local elements = vector;
+		if(bAsInt) then
+			elements = {};
+			for i, value in ipairs(vector) do
+				elements[#elements + 1] = string.format("%i", value);
+			end
+		end
+		
+		array[#array + 1] = "        " .. table.concat(elements, " ");
+	end
+	
+	return table.concat(array, "\n");
+end
+
+local nearZ = -2.0;
+local farZ = -8.0;
+local midZ = (nearZ + farZ) / 2.0;
+
+local posEquilateral =
+{
+	vmath.vec3( -1.0,  1.0, nearZ),
+	vmath.vec3( -1.0, -1.0, nearZ),
+	vmath.vec3(  1.0, -1.0, nearZ),
+	vmath.vec3(  1.0,  1.0, nearZ),
+
+	vmath.vec3( -1.0,  1.0, midZ),
+	vmath.vec3( -1.0, -1.0, midZ),
+	vmath.vec3(  1.0, -1.0, midZ),
+	vmath.vec3(  1.0,  1.0, midZ),
+
+	vmath.vec3( -1.0,  1.0, midZ),
+	vmath.vec3( -1.0, -1.0, midZ),
+	vmath.vec3(  1.0, -1.0, midZ),
+	vmath.vec3(  1.0,  1.0, midZ),
+
+	vmath.vec3( -1.0,  1.0, farZ),
+	vmath.vec3( -1.0, -1.0, farZ),
+	vmath.vec3(  1.0, -1.0, farZ),
+	vmath.vec3(  1.0,  1.0, farZ),
+};
+
+local function FauxProject(theVec)
+	return vmath.vec3(theVec);
+end
+
+local posFaux = {}
+
+for i, value in ipairs(posEquilateral) do
+	if(value.z == nearZ) then
+		posFaux[#posFaux + 1] = FauxProject(value);
+	else
+		local ratio = nearZ / value.z;
+		posFaux[#posFaux + 1] = value * ratio;
+--		posFaux[#posFaux].z = nearZ;
+	end
+end
+
+local nearColor = vmath.vec4(1.0, 0.0, 0.0, 1.0);
+local farColor = vmath.vec4(0.0, 1.0, 0.0, 1.0);
+local midColor = (nearColor + farColor) / 2.0;
+
+local colors =
+{
+	nearColor,
+	nearColor,
+	nearColor,
+	nearColor,
+	
+	midColor,
+	midColor,
+	midColor,
+	midColor,
+
+	midColor,
+	midColor,
+	midColor,
+	midColor,
+
+	farColor,
+	farColor,
+	farColor,
+	farColor,
+}
+
+local indices =
+{
+	0, 1, 4,
+	1, 5, 4,
+	
+	1, 2, 6,
+	1, 6, 5,
+	
+	2, 6, 3,
+	3, 6, 7,
+
+	8, 9, 12,
+	9, 13, 12,
+	
+	9, 10, 14,
+	9, 14, 13,
+	
+	10, 14, 11,
+	11, 14, 15,
+}
+
+do
+	local writer = XmlWriter.XmlWriter("RealHallway.xml");
+	writer:AddPI("oxygen", [[RNGSchema="../../Documents/meshFormat.rnc" type="compact"]]);
+	writer:PushElement("mesh", "http://www.arcsynthesis.com/gltut/mesh");
+		writer:PushElement("attribute");
+			writer:AddAttribute("index", "0");
+			writer:AddAttribute("type", "float");
+			writer:AddAttribute("size", "3");
+			writer:AddText(GenStringFromArray(posEquilateral));
+		writer:PopElement();
+		writer:PushElement("attribute");
+			writer:AddAttribute("index", "1");
+			writer:AddAttribute("type", "float");
+			writer:AddAttribute("size", "4");
+			writer:AddText(GenStringFromArray(colors));
+		writer:PopElement();
+		writer:PushElement("indices");
+			writer:AddAttribute("cmd", "triangles");
+			writer:AddAttribute("type", "ushort");
+			writer:AddText(table.concat(indices, " "));
+		writer:PopElement();
+	writer:PopElement();
+	writer:Close();
+end
+
+do
+	local writer = XmlWriter.XmlWriter("FauxHallway.xml");
+	writer:AddPI("oxygen", [[RNGSchema="../../Documents/meshFormat.rnc" type="compact"]]);
+	writer:PushElement("mesh", "http://www.arcsynthesis.com/gltut/mesh");
+		writer:PushElement("attribute");
+			writer:AddAttribute("index", "0");
+			writer:AddAttribute("type", "float");
+			writer:AddAttribute("size", "3");
+			writer:AddText(GenStringFromArray(posFaux));
+		writer:PopElement();
+		writer:PushElement("attribute");
+			writer:AddAttribute("index", "1");
+			writer:AddAttribute("type", "float");
+			writer:AddAttribute("size", "4");
+			writer:AddText(GenStringFromArray(colors));
+		writer:PopElement();
+		writer:PushElement("indices");
+			writer:AddAttribute("cmd", "triangles");
+			writer:AddAttribute("type", "ushort");
+			writer:AddText(table.concat(indices, " "));
+		writer:PopElement();
+	writer:PopElement();
+	writer:Close();
+end

File Tut 09 Plane Lights/data/GenPlane.lua

+require "XmlWriter"
+require "vmath"
+
+local function GenStringFromArray(theArray, bAsInt)
+	local array = {" "}
+	for i, vector in ipairs(theArray) do
+		local elements = vector;
+		if(bAsInt) then
+			elements = {};
+			for i, value in ipairs(vector) do
+				elements[#elements + 1] = string.format("%i", value);
+			end
+		end
+		
+		array[#array + 1] = "        " .. table.concat(vector, " ");
+	end
+	
+	return table.concat(array, "\n");
+end
+
+local positions =
+{
+	vmath.vec3(0.5, 0.0, -0.5),
+	vmath.vec3(0.5, 0.0, 0.5),
+	vmath.vec3(-0.5, 0.0, 0.5),
+	vmath.vec3(-0.5, 0.0, -0.5),
+
+	vmath.vec3(0.5, 0.0, -0.5),
+	vmath.vec3(0.5, 0.0, 0.5),
+	vmath.vec3(-0.5, 0.0, 0.5),
+	vmath.vec3(-0.5, 0.0, -0.5),
+};
+
+local largePositions =
+{
+	vmath.vec3(30.0, 0.0, -30.0),
+	vmath.vec3(30.0, 0.0, 30.0),
+	vmath.vec3(-30.0, 0.0, 30.0),
+	vmath.vec3(-30.0, 0.0, -30.0),
+
+	vmath.vec3(30.0, 0.0, -30.0),
+	vmath.vec3(30.0, 0.0, 30.0),
+	vmath.vec3(-30.0, 0.0, 30.0),
+	vmath.vec3(-30.0, 0.0, -30.0),
+};
+
+local normals =
+{
+	vmath.vec3(0.0, 1.0, 0.0),
+	vmath.vec3(0.0, 1.0, 0.0),
+	vmath.vec3(0.0, 1.0, 0.0),
+	vmath.vec3(0.0, 1.0, 0.0),
+
+	vmath.vec3(0.0, -1.0, 0.0),
+	vmath.vec3(0.0, -1.0, 0.0),
+	vmath.vec3(0.0, -1.0, 0.0),
+	vmath.vec3(0.0, -1.0, 0.0),
+};
+
+local indices =
+{
+	vmath.vec3(0, 1, 2),
+	vmath.vec3(2, 3, 0),
+
+	vmath.vec3(4, 6, 5),
+	vmath.vec3(6, 4, 7),
+};
+
+do
+	local writer = XmlWriter.XmlWriter("UnitPlane.xml");
+	writer:AddPI("oxygen", [[RNGSchema="../../Documents/meshFormat.rnc" type="compact"]]);
+	writer:PushElement("mesh", "http://www.arcsynthesis.com/gltut/mesh");
+		writer:PushElement("attribute");
+			writer:AddAttribute("index", "0");
+			writer:AddAttribute("type", "float");
+			writer:AddAttribute("size", "3");
+			writer:AddText(GenStringFromArray(positions));
+		writer:PopElement();
+		writer:PushElement("attribute");
+			writer:AddAttribute("index", "2");
+			writer:AddAttribute("type", "float");
+			writer:AddAttribute("size", "3");
+			writer:AddText(GenStringFromArray(normals));
+		writer:PopElement();
+		writer:PushElement("indices");
+			writer:AddAttribute("cmd", "triangles");
+			writer:AddAttribute("type", "ushort");
+			writer:AddText(GenStringFromArray(indices, true));
+		writer:PopElement();
+	writer:PopElement();
+	writer:Close();
+end
+
+do
+	local writer = XmlWriter.XmlWriter("LargePlane.xml");
+	writer:AddPI("oxygen", [[RNGSchema="../../Documents/meshFormat.rnc" type="compact"]]);
+	writer:PushElement("mesh", "http://www.arcsynthesis.com/gltut/mesh");
+		writer:PushElement("attribute");
+			writer:AddAttribute("index", "0");
+			writer:AddAttribute("type", "float");
+			writer:AddAttribute("size", "3");
+			writer:AddText(GenStringFromArray(largePositions));
+		writer:PopElement();
+		writer:PushElement("attribute");
+			writer:AddAttribute("index", "2");
+			writer:AddAttribute("type", "float");
+			writer:AddAttribute("size", "3");
+			writer:AddText(GenStringFromArray(normals));
+		writer:PopElement();
+		writer:PushElement("indices");
+			writer:AddAttribute("cmd", "triangles");
+			writer:AddAttribute("type", "ushort");
+			writer:AddText(GenStringFromArray(indices, true));
+		writer:PopElement();
+	writer:PopElement();
+	writer:Close();
+end

File Tut 09 Plane Lights/data/LargePlane.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<?oxygen RNGSchema="../../Documents/meshFormat.rnc" type="compact"?>
+
+<mesh xmlns="http://www.arcsynthesis.com/gltut/mesh" >
+	<attribute index="0" type="float" size="3" > 
+        30 0 -30
+        30 0 30
+        -30 0 30
+        -30 0 -30
+        30 0 -30
+        30 0 30
+        -30 0 30
+        -30 0 -30</attribute>
+	<attribute index="2" type="float" size="3" > 
+        0 1 0
+        0 1 0
+        0 1 0
+        0 1 0
+        0 -1 0
+        0 -1 0
+        0 -1 0
+        0 -1 0</attribute>
+	<indices cmd="triangles" type="ushort" > 
+        0 1 2
+        2 3 0
+        4 6 5
+        6 4 7</indices>
+</mesh>

File Tut 09 Plane Lights/data/NoCorrectVertexColors.frag

+#version 330
+
+noperspective in vec4 theColor;
+
+out vec4 outputColor;
+
+void main()
+{
+	outputColor = theColor;
+}

File Tut 09 Plane Lights/data/NoCorrectVertexColors.vert

+#version 330
+
+layout (location = 0) in vec3 position;
+layout (location = 1) in vec4 color;
+
+noperspective out vec4 theColor;
+
+uniform mat4 cameraToClipMatrix;
+
+void main()
+{
+	gl_Position = cameraToClipMatrix * vec4(position, 1.0);
+	theColor = color;
+}

File Tut 09 Plane Lights/data/PosTransform.vert

+#version 330
+
+layout(location = 0) in vec3 position;
+
+uniform mat4 cameraToClipMatrix;
+uniform mat4 modelToCameraMatrix;
+
+void main()
+{
+	gl_Position = cameraToClipMatrix * (modelToCameraMatrix * vec4(position, 1.0));
+}

File Tut 09 Plane Lights/data/PosVertexLighting_PCN.vert

+#version 330
+
+layout(location = 0) in vec3 position;
+layout(location = 1) in vec4 diffuseColor;
+layout(location = 2) in vec3 normal;
+
+smooth out vec4 interpColor;
+
+uniform vec3 lightPos;
+uniform vec4 lightIntensity;
+uniform vec4 ambientIntensity;
+
+uniform mat4 cameraToClipMatrix;
+uniform mat4 modelToCameraMatrix;
+
+uniform mat3 normalModelToCameraMatrix;
+
+void main()
+{
+	vec4 cameraPosition = (modelToCameraMatrix * vec4(position, 1.0));
+	gl_Position = cameraToClipMatrix * cameraPosition;
+
+	vec3 normCamSpace = normalize(normalModelToCameraMatrix * normal);
+	
+	vec3 dirToLight = normalize(lightPos - vec3(cameraPosition));
+	
+	float cosAngIncidence = dot(normCamSpace, dirToLight);
+	cosAngIncidence = clamp(cosAngIncidence, 0, 1);
+	
+	interpColor = (diffuseColor * lightIntensity * cosAngIncidence) +
+		(diffuseColor * ambientIntensity);
+}

File Tut 09 Plane Lights/data/PosVertexLighting_PN.vert

+#version 330
+
+layout(location = 0) in vec3 position;
+layout(location = 2) in vec3 normal;
+
+smooth out vec4 interpColor;
+
+uniform vec3 lightPos;
+uniform vec4 lightIntensity;
+uniform vec4 ambientIntensity;
+
+uniform mat4 cameraToClipMatrix;
+uniform mat4 modelToCameraMatrix;
+
+uniform mat3 normalModelToCameraMatrix;
+
+void main()
+{
+	vec4 cameraPosition = (modelToCameraMatrix * vec4(position, 1.0));
+	gl_Position = cameraToClipMatrix * cameraPosition;
+
+	vec3 normCamSpace = normalize(normalModelToCameraMatrix * normal);
+	
+	vec3 dirToLight = normalize(lightPos - vec3(cameraPosition));
+	
+	float cosAngIncidence = dot(normCamSpace, dirToLight);
+	cosAngIncidence = clamp(cosAngIncidence, 0, 1);
+	
+	interpColor = (lightIntensity * cosAngIncidence) + ambientIntensity;
+}

File Tut 09 Plane Lights/data/RealHallway.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<?oxygen RNGSchema="../../Documents/meshFormat.rnc" type="compact"?>
+
+<mesh xmlns="http://www.arcsynthesis.com/gltut/mesh" >
+	<attribute index="0" type="float" size="3" > 
+        -1 1 -2
+        -1 -1 -2
+        1 -1 -2
+        1 1 -2
+        -1 1 -5
+        -1 -1 -5
+        1 -1 -5
+        1 1 -5
+        -1 1 -5
+        -1 -1 -5
+        1 -1 -5
+        1 1 -5
+        -1 1 -8
+        -1 -1 -8
+        1 -1 -8
+        1 1 -8</attribute>
+	<attribute index="1" type="float" size="4" > 
+        1 0 0 1
+        1 0 0 1
+        1 0 0 1
+        1 0 0 1
+        0.5 0.5 0 1
+        0.5 0.5 0 1
+        0.5 0.5 0 1
+        0.5 0.5 0 1
+        0.5 0.5 0 1
+        0.5 0.5 0 1
+        0.5 0.5 0 1
+        0.5 0.5 0 1
+        0 1 0 1
+        0 1 0 1
+        0 1 0 1
+        0 1 0 1</attribute>
+	<indices cmd="triangles" type="ushort" >0 1 4 1 5 4 1 2 6 1 6 5 2 6 3 3 6 7 8 9 12 9 13 12 9 10 14 9 14 13 10 14 11 11 14 15</indices>
+</mesh>

File Tut 09 Plane Lights/data/SmoothVertexColors.frag

+#version 330
+
+smooth in vec4 theColor;
+
+out vec4 outputColor;
+
+void main()
+{
+	outputColor = theColor;
+}

File Tut 09 Plane Lights/data/SmoothVertexColors.vert

+#version 330
+
+layout (location = 0) in vec3 position;
+layout (location = 1) in vec4 color;
+
+smooth out vec4 theColor;
+
+uniform mat4 cameraToClipMatrix;
+
+void main()
+{
+	gl_Position = cameraToClipMatrix * vec4(position, 1.0);
+	theColor = color;
+}

File Tut 09 Plane Lights/data/UniformColor.frag

+#version 330
+
+uniform vec4 objectColor;
+
+out vec4 outputColor;
+
+void main()
+{
+	outputColor = objectColor;
+}

File Tut 09 Plane Lights/data/UnitCube.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<?oxygen RNGSchema="../../Documents/meshFormat.rnc" type="compact"?>
+
+<mesh xmlns="http://www.arcsynthesis.com/gltut/mesh" >
+	<attribute index="0" type="float" size="3" > 
+        0.5 0.5 0.5
+        0.5 -0.5 0.5
+        -0.5 -0.5 0.5
+        -0.5 0.5 0.5
+        0.5 0.5 0.5
+        -0.5 0.5 0.5
+        -0.5 0.5 -0.5
+        0.5 0.5 -0.5
+        0.5 0.5 0.5
+        0.5 0.5 -0.5
+        0.5 -0.5 -0.5
+        0.5 -0.5 0.5
+        0.5 0.5 -0.5
+        -0.5 0.5 -0.5
+        -0.5 -0.5 -0.5
+        0.5 -0.5 -0.5
+        0.5 -0.5 0.5
+        0.5 -0.5 -0.5
+        -0.5 -0.5 -0.5
+        -0.5 -0.5 0.5
+        -0.5 0.5 0.5
+        -0.5 -0.5 0.5
+        -0.5 -0.5 -0.5
+        -0.5 0.5 -0.5</attribute>
+	<attribute index="1" type="float" size="4" > 
+        0 1 0 1
+        0 1 0 1
+        0 1 0 1
+        0 1 0 1
+        0 0 1 1
+        0 0 1 1
+        0 0 1 1
+        0 0 1 1
+        1 0 0 1
+        1 0 0 1
+        1 0 0 1
+        1 0 0 1
+        1 1 0 1
+        1 1 0 1
+        1 1 0 1
+        1 1 0 1
+        0 1 1 1
+        0 1 1 1
+        0 1 1 1
+        0 1 1 1
+        1 0 1 1
+        1 0 1 1
+        1 0 1 1
+        1 0 1 1</attribute>
+	<vao name="color" >
+		<source attrib="0" />
+		<source attrib="1" />
+	</vao>
+	<vao name="flat" >
+		<source attrib="0" />
+	</vao>
+	<indices cmd="triangles" type="ushort" > 
+        0 1 2
+        2 3 0
+        4 5 6
+        6 7 4
+        8 9 10
+        10 11 8
+        12 13 14
+        14 15 12
+        16 17 18
+        18 19 16
+        20 21 22
+        22 23 20</indices>
+</mesh>

File Tut 09 Plane Lights/data/UnitCylinder.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<?oxygen RNGSchema="../../Documents/meshFormat.rnc" type="compact"?>
+
+<mesh xmlns="http://www.arcsynthesis.com/gltut/mesh" >
+	<attribute index="0" type="float" size="3" > 
+        0 0.5 0
+        0.5 0.5 0
+        0.5 -0.5 0
+        0.48907381875731 0.5 0.1039557588888
+        0.48907381875731 -0.5 0.1039557588888
+        0.45677280077542 0.5 0.20336815992623
+        0.45677280077542 -0.5 0.20336815992623
+        0.40450865316151 0.5 0.29389241146627
+        0.40450865316151 -0.5 0.29389241146627
+        0.33456556611288 0.5 0.37157217599218
+        0.33456556611288 -0.5 0.37157217599218
+        0.2500003830126 0.5 0.43301248075957
+        0.2500003830126 -0.5 0.43301248075957
+        0.15450900193016 0.5 0.47552809414644
+        0.15450900193016 -0.5 0.47552809414644
+        0.052264847412855 0.5 0.49726088296277
+        0.052264847412855 -0.5 0.49726088296277
+        -0.052263527886268 0.5 0.49726102165048
+        -0.052263527886268 -0.5 0.49726102165048
+        -0.15450774007312 0.5 0.47552850414828
+        -0.15450774007312 -0.5 0.47552850414828
+        -0.24999923397422 0.5 0.43301314415651
+        -0.24999923397422 -0.5 0.43301314415651
+        -0.33456458011157 0.5 0.37157306379065
+        -0.33456458011157 -0.5 0.37157306379065
+        -0.40450787329018 0.5 0.29389348486527
+        -0.40450787329018 -0.5 0.29389348486527
+        -0.45677226111814 0.5 0.20336937201315
+        -0.45677226111814 -0.5 0.20336937201315
+        -0.48907354289964 0.5 0.10395705668972
+        -0.48907354289964 -0.5 0.10395705668972
+        -0.49999999999824 0.5 1.3267948966764e-006
+        -0.49999999999824 -0.5 1.3267948966764e-006
+        -0.48907409461153 0.5 -0.10395446108714
+        -0.48907409461153 -0.5 -0.10395446108714
+        -0.45677334042948 0.5 -0.20336694783787
+        -0.45677334042948 -0.5 -0.20336694783787
+        -0.40450943302999 0.5 -0.2938913380652
+        -0.40450943302999 -0.5 -0.2938913380652
+        -0.33456655211184 0.5 -0.3715712881911
+        -0.33456655211184 -0.5 -0.3715712881911
+        -0.25000153204922 0.5 -0.43301181735958
+        -0.25000153204922 -0.5 -0.43301181735958
+        -0.15451026378611 0.5 -0.47552768414126
+        -0.15451026378611 -0.5 -0.47552768414126
+        -0.052266166939075 0.5 -0.49726074427155
+        -0.052266166939075 -0.5 -0.49726074427155
+        0.052262208359312 0.5 -0.4972611603347
+        0.052262208359312 -0.5 -0.4972611603347
+        0.15450647821499 0.5 -0.47552891414676
+        0.15450647821499 -0.5 -0.47552891414676
+        0.24999808493408 0.5 -0.4330138075504
+        0.24999808493408 -0.5 -0.4330138075504
+        0.3345635941079 0.5 -0.37157395158649
+        0.3345635941079 -0.5 -0.37157395158649
+        0.40450709341601 0.5 -0.2938945582622
+        0.40450709341601 -0.5 -0.2938945582622
+        0.45677172145764 0.5 -0.20337058409865
+        0.45677172145764 -0.5 -0.20337058409865
+        0.48907326703854 0.5 -0.10395835448992
+        0.48907326703854 -0.5 -0.10395835448992
+        0 -0.5 0
+        0.5 0.5 0
+        0.5 -0.5 0
+        0.48907381875731 0.5 0.1039557588888
+        0.48907381875731 -0.5 0.1039557588888
+        0.45677280077542 0.5 0.20336815992623
+        0.45677280077542 -0.5 0.20336815992623
+        0.40450865316151 0.5 0.29389241146627
+        0.40450865316151 -0.5 0.29389241146627
+        0.33456556611288 0.5 0.37157217599218
+        0.33456556611288 -0.5 0.37157217599218
+        0.2500003830126 0.5 0.43301248075957
+        0.2500003830126 -0.5 0.43301248075957
+        0.15450900193016 0.5 0.47552809414644
+        0.15450900193016 -0.5 0.47552809414644
+        0.052264847412855 0.5 0.49726088296277
+        0.052264847412855 -0.5 0.49726088296277
+        -0.052263527886268 0.5 0.49726102165048
+        -0.052263527886268 -0.5 0.49726102165048
+        -0.15450774007312 0.5 0.47552850414828
+        -0.15450774007312 -0.5 0.47552850414828
+        -0.24999923397422 0.5 0.43301314415651
+        -0.24999923397422 -0.5 0.43301314415651
+        -0.33456458011157 0.5 0.37157306379065
+        -0.33456458011157 -0.5 0.37157306379065
+        -0.40450787329018 0.5 0.29389348486527
+        -0.40450787329018 -0.5 0.29389348486527
+        -0.45677226111814 0.5 0.20336937201315
+        -0.45677226111814 -0.5 0.20336937201315
+        -0.48907354289964 0.5 0.10395705668972
+        -0.48907354289964 -0.5 0.10395705668972
+        -0.49999999999824 0.5 1.3267948966764e-006
+        -0.49999999999824 -0.5 1.3267948966764e-006
+        -0.48907409461153 0.5 -0.10395446108714
+        -0.48907409461153 -0.5 -0.10395446108714
+        -0.45677334042948 0.5 -0.20336694783787
+        -0.45677334042948 -0.5 -0.20336694783787
+        -0.40450943302999 0.5 -0.2938913380652
+        -0.40450943302999 -0.5 -0.2938913380652
+        -0.33456655211184 0.5 -0.3715712881911
+        -0.33456655211184 -0.5 -0.3715712881911
+        -0.25000153204922 0.5 -0.43301181735958
+        -0.25000153204922 -0.5 -0.43301181735958
+        -0.15451026378611 0.5 -0.47552768414126
+        -0.15451026378611 -0.5 -0.47552768414126
+        -0.052266166939075 0.5 -0.49726074427155
+        -0.052266166939075 -0.5 -0.49726074427155
+        0.052262208359312 0.5 -0.4972611603347
+        0.052262208359312 -0.5 -0.4972611603347
+        0.15450647821499 0.5 -0.47552891414676
+        0.15450647821499 -0.5 -0.47552891414676
+        0.24999808493408 0.5 -0.4330138075504
+        0.24999808493408 -0.5 -0.4330138075504
+        0.3345635941079 0.5 -0.37157395158649
+        0.3345635941079 -0.5 -0.37157395158649
+        0.40450709341601 0.5 -0.2938945582622
+        0.40450709341601 -0.5 -0.2938945582622
+        0.45677172145764 0.5 -0.20337058409865
+        0.45677172145764 -0.5 -0.20337058409865
+        0.48907326703854 0.5 -0.10395835448992
+        0.48907326703854 -0.5 -0.10395835448992</attribute>
+	<attribute index="1" type="float" size="4" > 
+        1 1 1 1
+        0.9 0.5 0.5 1
+        0.9 0.5 0.5 1
+        0.82 0.42 0.42 1
+        0.82 0.42 0.42 1
+        0.74 0.34 0.34 1
+        0.74 0.34 0.34 1
+        0.66 0.26 0.26 1
+        0.66 0.26 0.26 1
+        0.58 0.18 0.18 1
+        0.58 0.18 0.18 1
+        0.5 0.1 0.1 1
+        0.5 0.1 0.1 1
+        0.58 0.18 0.18 1
+        0.58 0.18 0.18 1
+        0.66 0.26 0.26 1
+        0.66 0.26 0.26 1
+        0.74 0.34 0.34 1
+        0.74 0.34 0.34 1
+        0.82 0.42 0.42 1
+        0.82 0.42 0.42 1
+        0.9 0.5 0.5 1
+        0.9 0.5 0.5 1
+        0.82 0.42 0.42 1
+        0.82 0.42 0.42 1
+        0.74 0.34 0.34 1
+        0.74 0.34 0.34 1
+        0.66 0.26 0.26 1
+        0.66 0.26 0.26 1
+        0.58 0.18 0.18 1
+        0.58 0.18 0.18 1
+        0.5 0.1 0.1 1
+        0.5 0.1 0.1 1
+        0.58 0.18 0.18 1
+        0.58 0.18 0.18 1
+        0.66 0.26 0.26 1
+        0.66 0.26 0.26 1
+        0.74 0.34 0.34 1
+        0.74 0.34 0.34 1
+        0.82 0.42 0.42 1
+        0.82 0.42 0.42 1
+        0.9 0.5 0.5 1
+        0.9 0.5 0.5 1
+        0.82 0.42 0.42 1
+        0.82 0.42 0.42 1
+        0.74 0.34 0.34 1
+        0.74 0.34 0.34 1
+        0.66 0.26 0.26 1
+        0.66 0.26 0.26 1
+        0.58 0.18 0.18 1
+        0.58 0.18 0.18 1
+        0.5 0.1 0.1 1
+        0.5 0.1 0.1 1
+        0.58 0.18 0.18 1
+        0.58 0.18 0.18 1
+        0.66 0.26 0.26 1
+        0.66 0.26 0.26 1
+        0.74 0.34 0.34 1
+        0.74 0.34 0.34 1
+        0.82 0.42 0.42 1
+        0.82 0.42 0.42 1
+        1 1 1 1
+        0.9 0.5 0.5 1
+        0.9 0.5 0.5 1
+        0.82 0.42 0.42 1
+        0.82 0.42 0.42 1
+        0.74 0.34 0.34 1
+        0.74 0.34 0.34 1
+        0.66 0.26 0.26 1
+        0.66 0.26 0.26 1
+        0.58 0.18 0.18 1
+        0.58 0.18 0.18 1
+        0.5 0.1 0.1 1
+        0.5 0.1 0.1 1
+        0.58 0.18 0.18 1
+        0.58 0.18 0.18 1
+        0.66 0.26 0.26 1
+        0.66 0.26 0.26 1
+        0.74 0.34 0.34 1
+        0.74 0.34 0.34 1
+        0.82 0.42 0.42 1
+        0.82 0.42 0.42 1
+        0.9 0.5 0.5 1
+        0.9 0.5 0.5 1
+        0.82 0.42 0.42 1
+        0.82 0.42 0.42 1
+        0.74 0.34 0.34 1
+        0.74 0.34 0.34 1
+        0.66 0.26 0.26 1
+        0.66 0.26 0.26 1
+        0.58 0.18 0.18 1
+        0.58 0.18 0.18 1
+        0.5 0.1 0.1 1
+        0.5 0.1 0.1 1
+        0.58 0.18 0.18 1
+        0.58 0.18 0.18 1
+        0.66 0.26 0.26 1
+        0.66 0.26 0.26 1
+        0.74 0.34 0.34 1
+        0.74 0.34 0.34 1
+        0.82 0.42 0.42 1
+        0.82 0.42 0.42 1
+        0.9 0.5 0.5 1
+        0.9 0.5 0.5 1
+        0.82 0.42 0.42 1
+        0.82 0.42 0.42 1
+        0.74 0.34 0.34 1
+        0.74 0.34 0.34 1
+        0.66 0.26 0.26 1
+        0.66 0.26 0.26 1
+        0.58 0.18 0.18 1
+        0.58 0.18 0.18 1
+        0.5 0.1 0.1 1
+        0.5 0.1 0.1 1
+        0.58 0.18 0.18 1
+        0.58 0.18 0.18 1
+        0.66 0.26 0.26 1
+        0.66 0.26 0.26 1
+        0.74 0.34 0.34 1
+        0.74 0.34 0.34 1
+        0.82 0.42 0.42 1
+        0.82 0.42 0.42 1</attribute>
+	<attribute index="2" type="float" size="3" > 
+        0 1 0
+        0 1 0
+        0 -1 0
+        0 1 0
+        0 -1 0
+        0 1 0
+        0 -1 0
+        0 1 0
+        0 -1 0
+        0 1 0
+        0 -1 0
+        0 1 0
+        0 -1 0
+        0 1 0
+        0 -1 0
+        0 1 0
+        0 -1 0
+        0 1 0
+        0 -1 0
+        0 1 0
+        0 -1 0
+        0 1 0
+        0 -1 0
+        0 1 0
+        0 -1 0
+        0 1 0
+        0 -1 0
+        0 1 0
+        0 -1 0
+        0 1 0
+        0 -1 0
+        0 1 0
+        0 -1 0
+        0 1 0
+        0 -1 0
+        0 1 0
+        0 -1 0
+        0 1 0
+        0 -1 0
+        0 1 0
+        0 -1 0
+        0 1 0
+        0 -1 0
+        0 1 0
+        0 -1 0
+        0 1 0
+        0 -1 0
+        0 1 0
+        0 -1 0
+        0 1 0
+        0 -1 0
+        0 1 0
+        0 -1 0
+        0 1 0
+        0 -1 0
+        0 1 0
+        0 -1 0
+        0 1 0
+        0 -1 0
+        0 1 0
+        0 -1 0
+        0 -1 0
+        1 0 0
+        1 0 0