Commits

Jason McKesson committed 2b9f51d

Tut16: Light Environemnt seems to work. Just need to choose a good one.

  • Participants
  • Parent commits 913b011

Comments (0)

Files changed (11)

File Tut 16 Gamma and Textures/Gamma Landscape.cpp

 #include "../framework/Timer.h"
 #include "../framework/UniformBlockArray.h"
 #include "../framework/directories.h"
+#include "../framework/MousePole.h"
+#include "../framework/Interpolators.h"
+#include "LightEnv.h"
 #include <glm/glm.hpp>
 #include <glm/gtc/type_ptr.hpp>
 #include <glm/gtc/matrix_transform.hpp>
 	GLuint theProgram;
 
 	GLuint modelToCameraMatrixUnif;
+	GLuint numberOfLightsUnif;
 };
 
 struct UnlitProgData
 UnlitProgData g_progUnlit;
 
 const int g_projectionBlockIndex = 0;
-const int g_lightBlockIndex = 0;
+const int g_lightBlockIndex = 1;
 const int g_colorTexUnit = 0;
 
 ProgramData LoadProgram(const std::string &strVertexShader, const std::string &strFragmentShader)
 	ProgramData data;
 	data.theProgram = Framework::CreateProgram(shaderList);
 	data.modelToCameraMatrixUnif = glGetUniformLocation(data.theProgram, "modelToCameraMatrix");
+	data.numberOfLightsUnif = glGetUniformLocation(data.theProgram, "numberOfLights");
 
 	GLuint projectionBlock = glGetUniformBlockIndex(data.theProgram, "Projection");
 	glUniformBlockBinding(data.theProgram, projectionBlock, g_projectionBlockIndex);
 
+	GLuint lightBlockIndex = glGetUniformBlockIndex(data.theProgram, "Light");
+	glUniformBlockBinding(data.theProgram, lightBlockIndex, g_lightBlockIndex);
+
 	GLuint colorTextureUnif = glGetUniformLocation(data.theProgram, "diffuseColorTex");
 	glUseProgram(data.theProgram);
 	glUniform1i(colorTextureUnif, g_colorTexUnit);
 };
 
 GLuint g_projectionUniformBuffer = 0;
+GLuint g_lightUniformBuffer = 0;
 GLuint g_linearTexture = 0;
 // GLuint g_gammaTexture = 0;
 
 
 glutil::ViewScale g_initialViewScale =
 {
-	5.0f,
-	90.0f,
-	2.0f,
-	0.5f,
-	4.0f,
-	1.0f,
+	5.0f, 90.0f,
+	2.0f, 0.5f,
+	4.0f, 1.0f,
 	90.0f/250.0f
 };
 
 glutil::ViewPole g_viewPole(g_initialView, g_initialViewScale, glutil::MB_LEFT_BTN);
 
-
 namespace
 {
-	int calc_glut_modifiers()
-	{
-		int ret = 0;
+	void MouseMotion(int x, int y) { Framework::ForwardMouseMotion(g_viewPole, x, y); }
+	void MouseButton(int button, int state, int x, int y) { Framework::ForwardMouseButton(g_viewPole, button, state, x, y); }
+	void MouseWheel(int wheel, int direction, int x, int y) { Framework::ForwardMouseWheel(g_viewPole, wheel, direction, x, y); }
+}
 
-		int modifiers = glutGetModifiers();
-		if(modifiers & GLUT_ACTIVE_SHIFT)
-			ret |= glutil::MM_KEY_SHIFT;
-		if(modifiers & GLUT_ACTIVE_CTRL)
-			ret |= glutil::MM_KEY_CTRL;
-		if(modifiers & GLUT_ACTIVE_ALT)
-			ret |= glutil::MM_KEY_ALT;
-
-		return ret;
-	}
-
-	void MouseMotion(int x, int y)
-	{
-		g_viewPole.MouseMove(glm::ivec2(x, y));
-		glutPostRedisplay();
-	}
-
-
-	void MouseButton(int button, int state, int x, int y)
-	{
-		int modifiers = calc_glut_modifiers();
-
-		glm::ivec2 mouseLoc = glm::ivec2(x, y);
-
-		glutil::MouseButtons eButton;
-
-		switch(button)
-		{
-		case GLUT_LEFT_BUTTON:
-			eButton = glutil::MB_LEFT_BTN;
-			break;
-		case GLUT_MIDDLE_BUTTON:
-			eButton = glutil::MB_MIDDLE_BTN;
-			break;
-		case GLUT_RIGHT_BUTTON:
-			eButton = glutil::MB_RIGHT_BTN;
-			break;
-#ifdef LOAD_X11
-			//Linux Mouse wheel support
-		case 3:
-			{
-				g_viewPole.MouseWheel(1, modifiers, mouseLoc);
-				return;
-			}
-		case 4:
-			{
-				g_viewPole.MouseWheel(-1, modifiers, mouseLoc);
-				return;
-			}
-#endif
-		default:
-			return;
-		}
-
-		g_viewPole.MouseClick(eButton, state == GLUT_DOWN, modifiers, glm::ivec2(x, y));
-		glutPostRedisplay();
-	}
-
-	void MouseWheel(int wheel, int direction, int x, int y)
-	{
-		g_viewPole.MouseWheel(direction, calc_glut_modifiers(), glm::ivec2(x, y));
-		glutPostRedisplay();
-	}
-}
+LightEnv *g_pLightEnv = NULL;
 
 Framework::Mesh *g_pTerrain = NULL;
 Framework::Mesh *g_pSphere = NULL;
 {
 	try
 	{
+		g_pLightEnv = new LightEnv("LightEnv.xml");
+
 		InitializePrograms();
 
 		g_pTerrain = new Framework::Mesh("terrain.xml");
 	glBindBufferRange(GL_UNIFORM_BUFFER, g_projectionBlockIndex, g_projectionUniformBuffer,
 		0, sizeof(ProjectionBlock));
 
+	glGenBuffers(1, &g_lightUniformBuffer);
+	glBindBuffer(GL_UNIFORM_BUFFER, g_lightUniformBuffer);
+	glBufferData(GL_UNIFORM_BUFFER, sizeof(LightBlock), NULL, GL_STREAM_DRAW);
+
+	glBindBufferRange(GL_UNIFORM_BUFFER, g_lightBlockIndex, g_lightUniformBuffer,
+		0, sizeof(LightBlock));
+
 	glBindBuffer(GL_UNIFORM_BUFFER, 0);
 
 	LoadTextures();
 //If you need continuous updates of the screen, call glutPostRedisplay() at the end of the function.
 void display()
 {
-	glClearColor(0.75f, 0.75f, 1.0f, 1.0f);
-	glClearDepth(1.0f);
-	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 	if(g_useGammaDisplay)
 		glEnable(GL_FRAMEBUFFER_SRGB);
 	else
 		glDisable(GL_FRAMEBUFFER_SRGB);
 
+	g_pLightEnv->UpdateTime();
+
+	glm::vec4 bgColor = g_pLightEnv->GetBackgroundColor();
+	glClearColor(bgColor.x, bgColor.y, bgColor.z, bgColor.w);
+	glClearDepth(1.0f);
+	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+	glutil::MatrixStack modelMatrix;
+	modelMatrix.ApplyMatrix(g_viewPole.CalcMatrix());
+
+	LightBlock lightData = g_pLightEnv->GetLightBlock(g_viewPole.CalcMatrix());
+
+	glBindBuffer(GL_UNIFORM_BUFFER, g_lightUniformBuffer);
+	glBufferData(GL_UNIFORM_BUFFER, sizeof(LightBlock), &lightData, GL_STREAM_DRAW);
+	glBindBuffer(GL_UNIFORM_BUFFER, 0);
+
 	if(g_pSphere && g_pTerrain)
 	{
-		glutil::MatrixStack modelMatrix;
-
-		modelMatrix.ApplyMatrix(g_viewPole.CalcMatrix());
-
 		glutil::PushStack push(modelMatrix);
 		modelMatrix.RotateX(-90.0f);
 
 		glUseProgram(g_progStandard.theProgram);
 		glUniformMatrix4fv(g_progStandard.modelToCameraMatrixUnif, 1, GL_FALSE,
 			glm::value_ptr(modelMatrix.Top()));
+		glUniform1i(g_progStandard.numberOfLightsUnif, g_pLightEnv->GetNumLights());
 
 		glActiveTexture(GL_TEXTURE0 + g_colorTexUnit);
 		glBindTexture(GL_TEXTURE_2D, g_linearTexture);
 
 		push.ResetStack();
 
+		//Render the sun
+		{
+			glm::vec3 sunlightDir(g_pLightEnv->GetSunlightDirection());
+			modelMatrix.Translate(sunlightDir * 500.0f);
+			modelMatrix.Scale(30.0f, 30.0f, 30.0f);
+
+			glUseProgram(g_progUnlit.theProgram);
+			glUniformMatrix4fv(g_progUnlit.modelToCameraMatrixUnif, 1, GL_FALSE,
+				glm::value_ptr(modelMatrix.Top()));
+
+			glm::vec4 lightColor = g_pLightEnv->GetSunlightIntensity(), gamma;
+			glUniform4fv(g_progUnlit.objectColorUnif, 1, glm::value_ptr(lightColor));
+			g_pSphere->Render("flat");
+		}
+
+
+		push.ResetStack();
 
 		if(g_bDrawCameraPos)
 		{
 void reshape (int w, int h)
 {
 	glutil::MatrixStack persMatrix;
-	persMatrix.Perspective(60.0f, (h / (float)w), g_fzNear, g_fzFar);
+	persMatrix.Perspective(60.0f, (w / (float)h), g_fzNear, g_fzFar);
 
 	ProjectionBlock projData;
 	projData.cameraToClipMatrix = persMatrix.Top();
 	case 27:
 		delete g_pSphere;
 		delete g_pTerrain;
+		delete g_pLightEnv;
 		g_pSphere = NULL;
 		g_pTerrain = NULL;
+		g_pLightEnv = NULL;
 		glutLeaveMainLoop();
 		return;
 	case 'a':
 	case 32:
 		g_useGammaDisplay = !g_useGammaDisplay;
 		break;
+	case '-': g_pLightEnv->RewindTime(1.0f); break;
+	case '=': g_pLightEnv->FastForwardTime(1.0f); break;
 	case 't': g_bDrawCameraPos = !g_bDrawCameraPos; break;
+	case 'r':
+		{
+			bool isPaused = g_pLightEnv->IsPaused();
+			float elapsed = g_pLightEnv->GetElapsedTime();
+			delete g_pLightEnv;
+
+			g_pLightEnv = new LightEnv("LightEnv.xml");
+			g_pLightEnv->SetPause(isPaused);
+			g_pLightEnv->FastForwardTime(elapsed);
+
+			printf("Elapsed: %f\n", elapsed);
+		}
+		break;
+	case 'p':
+		g_pLightEnv->TogglePause();
+		break;
 	}
 
 	if(('1' <= key) && (key <= '9'))

File Tut 16 Gamma and Textures/LightEnv.cpp

+
+#include <algorithm>
+#include <fstream>
+#include <sstream>
+#include <utility>
+#include <vector>
+#include "LightEnv.h"
+#include "tinyxml.h"
+#include <glload/gl_all.h>
+#include "../framework/framework.h"
+#include <glm/gtc/matrix_transform.hpp>
+
+const float g_fHalfLightDistance = 20.0f;
+const float g_fLightAttenuation = 1.0f / (g_fHalfLightDistance * g_fHalfLightDistance);
+
+typedef std::pair<float, float> MaxIntensityData;
+typedef std::vector<MaxIntensityData> MaxIntensityVector;
+typedef std::pair<glm::vec4, float> LightData;
+typedef std::vector<LightData> LightVector;
+
+glm::vec4 GetValue(const LightData &data) {return data.first;}
+float GetTime(const LightData &data) {return data.second;}
+float GetValue(const MaxIntensityData &data) {return data.first;}
+float GetTime(const MaxIntensityData &data) {return data.second;}
+
+float distance(const glm::vec3 &lhs, const glm::vec3 &rhs)
+{
+	return glm::length(rhs - lhs);
+}
+
+
+namespace
+{
+	glm::vec4 ParseVec4(const std::string &strVec4)
+	{
+		std::stringstream strStream;
+		strStream << strVec4;
+		strStream.flush();
+		glm::vec4 ret;
+		strStream >> ret.x >> ret.y >> ret.z >> ret.w;
+		return ret;
+	}
+
+	glm::vec3 ParseVec3(const std::string &strVec3)
+	{
+		std::stringstream strStream;
+		strStream << strVec3;
+		strStream.flush();
+		glm::vec3 ret;
+		strStream >> ret.x >> ret.y >> ret.z;
+		return ret;
+	}
+}
+
+LightEnv::LightEnv( const std::string& envFilename )
+{
+	std::ifstream fileStream(envFilename.c_str());
+	if(!fileStream.is_open())
+		throw std::runtime_error("Could not find the mesh file.");
+
+	TiXmlDocument theDoc;
+
+	fileStream >> theDoc;
+	fileStream.close();
+
+	if(theDoc.Error())
+		throw std::runtime_error(theDoc.ErrorDesc());
+
+	TiXmlHandle docHandle(&theDoc);
+
+	const TiXmlElement *pSunNode = docHandle.FirstChild("lightenv").FirstChild("sun").ToElement();
+
+	if(!pSunNode)
+		throw std::runtime_error("There must be a 'lightenv' element that has a 'sun' element as a child.");
+
+	float timerTime = 0;
+	if(pSunNode->QueryFloatAttribute("time", &timerTime) != TIXML_SUCCESS)
+		throw std::runtime_error("'sun' elements must have a 'time' attribute that is a float.");
+
+	m_sunTimer = Framework::Timer(Framework::Timer::TT_LOOP, timerTime);
+
+	LightVector ambient;
+	LightVector light;
+	LightVector background;
+	MaxIntensityVector maxIntensity;
+
+	for(const TiXmlElement *pKeyElem = pSunNode->FirstChildElement("key");
+		pKeyElem;
+		pKeyElem = pKeyElem->NextSiblingElement("key"))
+	{
+		float keyTime = 0;
+		if(pKeyElem->QueryFloatAttribute("time", &keyTime) != TIXML_SUCCESS)
+			throw std::runtime_error("'key' elements must have a 'time' attribute that is a float.");
+		//Convert from hours to normalized time.
+		keyTime = keyTime / 24.0f;
+
+		std::string strVec4;
+		if(pKeyElem->QueryStringAttribute("ambient", &strVec4) != TIXML_SUCCESS)
+			throw std::runtime_error("'key' elements must have an 'ambient' attribute.");
+		ambient.push_back(LightData(ParseVec4(strVec4), keyTime));
+
+		if(pKeyElem->QueryStringAttribute("intensity", &strVec4) != TIXML_SUCCESS)
+			throw std::runtime_error("'key' elements must have a 'intensity' attribute.");
+		light.push_back(LightData(ParseVec4(strVec4), keyTime));
+
+		if(pKeyElem->QueryStringAttribute("background", &strVec4) != TIXML_SUCCESS)
+			throw std::runtime_error("'key' elements must have a 'background' attribute.");
+		background.push_back(LightData(ParseVec4(strVec4), keyTime));
+
+		maxIntensity.push_back(MaxIntensityData(0.0f, keyTime));
+		if(pKeyElem->QueryFloatAttribute("max-intensity", &maxIntensity.back().first) != TIXML_SUCCESS)
+			throw std::runtime_error("'key' elements must have a 'max-intensity' attribute that is a float.");
+	}
+
+	if(ambient.empty())
+		throw std::runtime_error("'sun' element must have at least one 'key' element child.");
+
+	m_ambientInterpolator.SetValues(ambient);
+	m_sunlightInterpolator.SetValues(light);
+	m_backgroundInterpolator.SetValues(background);
+	m_maxIntensityInterpolator.SetValues(maxIntensity);
+
+	const TiXmlElement *pLightNode = docHandle.FirstChild("lightenv").FirstChild("light").ToElement();
+	for(; pLightNode; pLightNode = pLightNode->NextSiblingElement("light"))
+	{
+		if(m_lightPos.size() + 1 == MAX_NUMBER_OF_LIGHTS)
+			throw std::runtime_error("Too many lights specified.");
+
+		float lightTime = 0;
+		if(pSunNode->QueryFloatAttribute("time", &lightTime) != TIXML_SUCCESS)
+			throw std::runtime_error("'light' elements must have a 'time' attribute that is a float.");
+
+		m_lightTimers.push_back(Framework::Timer(Framework::Timer::TT_LOOP, lightTime));
+
+		std::string strVec4;
+		if(pLightNode->QueryStringAttribute("intensity", &strVec4) != TIXML_SUCCESS)
+			throw std::runtime_error("'light' elements must have an 'intensity' attribute.");
+		m_lightIntensity.push_back(ParseVec4(strVec4));
+
+		std::vector<glm::vec3> posValues;
+		for(const TiXmlElement *pKeyElem = pLightNode->FirstChildElement("key");
+			pKeyElem;
+			pKeyElem = pKeyElem->NextSiblingElement("key"))
+		{
+			posValues.push_back(ParseVec3(pKeyElem->GetText()));
+		}
+
+		if(posValues.empty())
+			throw std::runtime_error("'light' elements must have at least one 'key' element child.");
+
+		m_lightPos.push_back(LightInterpolator());
+		m_lightPos.back().SetValues(posValues);
+	}
+}
+
+glm::vec4 LightEnv::GetSunlightDirection() const
+{
+	float angle = 2.0f * 3.14159f * m_sunTimer.GetAlpha();
+	glm::vec4 sunDirection(0.0f);
+	sunDirection[0] = sinf(angle);
+	sunDirection[1] = cosf(angle);
+
+	//Keep the sun from being perfectly centered overhead.
+	sunDirection = glm::rotate(glm::mat4(1.0f), 5.0f, glm::vec3(0.0f, 1.0f, 0.0f)) * sunDirection;
+
+	return sunDirection;
+}
+
+int LightEnv::GetNumLights() const
+{
+	return 1 + m_lightPos.size();
+}
+
+LightBlock LightEnv::GetLightBlock( const glm::mat4 &worldToCamera ) const
+{
+	LightBlock lightData;
+	lightData.ambientIntensity = m_ambientInterpolator.Interpolate(m_sunTimer.GetAlpha());
+	lightData.lightAttenuation = g_fLightAttenuation;
+	lightData.maxIntensity = m_maxIntensityInterpolator.Interpolate(m_sunTimer.GetAlpha());
+
+	lightData.lights[0].cameraSpaceLightPos =
+		worldToCamera * GetSunlightDirection();
+	lightData.lights[0].lightIntensity = m_sunlightInterpolator.Interpolate(m_sunTimer.GetAlpha());
+
+	for(size_t light = 0; light < m_lightPos.size(); light++)
+	{
+		glm::vec4 worldLightPos =
+			glm::vec4(m_lightPos[light].Interpolate(m_lightTimers[light].GetAlpha()), 1.0f);
+		glm::vec4 lightPosCameraSpace = worldToCamera * worldLightPos;
+
+		lightData.lights[light + 1].cameraSpaceLightPos = lightPosCameraSpace;
+		lightData.lights[light + 1].lightIntensity = m_lightIntensity[light];
+	}
+
+	return lightData;
+}
+
+namespace
+{
+	struct UpdateTimer
+	{
+		void operator()(Framework::Timer &timer) {timer.Update();}
+		void operator()(std::pair<const std::string, Framework::Timer> &timeData)
+		{timeData.second.Update();}
+	};
+
+	struct PauseTimer
+	{
+		PauseTimer(bool _pause) : pause(_pause) {}
+		void operator()(Framework::Timer &timer) {timer.SetPause(pause);}
+		void operator()(std::pair<const std::string, Framework::Timer> &timeData)
+		{timeData.second.SetPause(pause);}
+
+		bool pause;
+	};
+
+	struct RewindTimer
+	{
+		RewindTimer(float _secRewind) : secRewind(_secRewind) {}
+
+		void operator()(Framework::Timer &timer) {timer.Rewind(secRewind);}
+		void operator()(std::pair<const std::string, Framework::Timer> &timeData)
+		{timeData.second.Rewind(secRewind);}
+
+		float secRewind;
+	};
+
+	struct FFTimer
+	{
+		FFTimer(float _secFF) : secFF(_secFF) {}
+
+		void operator()(Framework::Timer &timer) {timer.Fastforward(secFF);}
+		void operator()(std::pair<const std::string, Framework::Timer> &timeData)
+		{timeData.second.Fastforward(secFF);}
+
+		float secFF;
+	};
+}
+
+void LightEnv::UpdateTime()
+{
+	m_sunTimer.Update();
+	std::for_each(m_lightTimers.begin(), m_lightTimers.end(), UpdateTimer());
+}
+
+void LightEnv::TogglePause()
+{
+	bool isPaused = m_sunTimer.TogglePause();
+	std::for_each(m_lightTimers.begin(), m_lightTimers.end(), PauseTimer(isPaused));
+}
+
+void LightEnv::SetPause( bool pause )
+{
+	m_sunTimer.SetPause(pause);
+	std::for_each(m_lightTimers.begin(), m_lightTimers.end(), PauseTimer(pause));
+}
+
+void LightEnv::RewindTime( float secRewind )
+{
+	m_sunTimer.Rewind(secRewind);
+	std::for_each(m_lightTimers.begin(), m_lightTimers.end(), RewindTimer(secRewind));
+}
+
+void LightEnv::FastForwardTime( float secFF )
+{
+	m_sunTimer.Fastforward(secFF);
+	std::for_each(m_lightTimers.begin(), m_lightTimers.end(), FFTimer(secFF));
+}

File Tut 16 Gamma and Textures/LightEnv.h

+
+#ifndef LIGHT_ENVIRONMENT_H
+#define LIGHT_ENVIRONMENT_H
+
+#include <map>
+#include <string>
+#include "../framework/Timer.h"
+#include <glm/glm.hpp>
+#include "../framework/Interpolators.h"
+
+//////////////////////////////////////////////////////////////////////////
+//Lighting Environment
+struct PerLight
+{
+	glm::vec4 cameraSpaceLightPos;
+	glm::vec4 lightIntensity;
+};
+
+const int MAX_NUMBER_OF_LIGHTS = 4;
+
+struct LightBlock
+{
+	glm::vec4 ambientIntensity;
+	float lightAttenuation;
+	float maxIntensity;
+	float padding[2];
+	PerLight lights[MAX_NUMBER_OF_LIGHTS];
+};
+
+class LightEnv
+{
+public:
+	LightEnv(const std::string &envFilename);
+
+	void UpdateTime();
+
+	void TogglePause();
+
+	void SetPause(bool pause = true);
+
+	bool IsPaused() const
+	{
+		return m_sunTimer.IsPaused();
+	}
+
+	void RewindTime(float secRewind);
+
+	void FastForwardTime(float secFF);
+
+	glm::vec4 GetBackgroundColor() const
+	{
+		return m_backgroundInterpolator.Interpolate(m_sunTimer.GetAlpha());
+	}
+
+	float GetMaxIntensity() const
+	{
+		return m_maxIntensityInterpolator.Interpolate(m_sunTimer.GetAlpha());
+	}
+
+	glm::vec4 GetSunlightDirection() const;
+
+	glm::vec4 GetSunlightIntensity() const
+	{
+		return m_sunlightInterpolator.Interpolate(m_sunTimer.GetAlpha());
+	}
+
+	float GetElapsedTime() const
+	{
+		return m_sunTimer.GetProgression();
+	}
+
+	int GetNumLights() const;
+
+	LightBlock GetLightBlock(const glm::mat4 &worldToCamera) const;
+
+private:
+	typedef Framework::ConstVelLinearInterpolator<glm::vec3> LightInterpolator;
+	typedef std::map<std::string, Framework::Timer> ExtraTimerMap;
+
+	Framework::Timer m_sunTimer;
+	Framework::TimedLinearInterpolator<glm::vec4> m_ambientInterpolator;
+	Framework::TimedLinearInterpolator<glm::vec4> m_backgroundInterpolator;
+	Framework::TimedLinearInterpolator<glm::vec4> m_sunlightInterpolator;
+	Framework::TimedLinearInterpolator<float> m_maxIntensityInterpolator;
+
+	std::vector<LightInterpolator> m_lightPos;
+	std::vector<glm::vec4> m_lightIntensity;
+	std::vector<Framework::Timer> m_lightTimers;
+};
+
+#endif //LIGHT_ENVIRONMENT_H

File Tut 16 Gamma and Textures/LightEnv.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<lightenv>
+    <sun time="15">
+        <key time="0" ambient="0.4 0.4 0.4 1.0" intensity="0 0 0 1.0" background="0.65 0.65
+            1.0 1.0" max-intensity="10" />
+        <key time="12" ambient="0.4 0.4 0.4 1.0" intensity="0 0 0 1.0" background="0.0 0.0
+            0.0 1.0" max-intensity="10" />
+    </sun>
+    <light time="30" intensity="16 16 16 1.0">
+        <key>0.0 40 0.0</key>
+    </light>
+</lightenv>

File Tut 16 Gamma and Textures/data/PNT.vert

 
 void main()
 {
-	gl_Position = cameraToClipMatrix * (modelToCameraMatrix * vec4(position, 1.0));
+	cameraSpacePosition = (modelToCameraMatrix * vec4(position, 1.0)).xyz;
+	gl_Position = cameraToClipMatrix * vec4(cameraSpacePosition, 1.0);
 	//Assume the modelToCameraMatrix contains no scaling.
 	cameraSpaceNormal = (modelToCameraMatrix * vec4(normal, 0)).xyz;
 	colorCoord = texCoord;

File Tut 16 Gamma and Textures/data/litTexture.frag

 	vec4 lightIntensity;
 };
 
-const int numberOfLights = 1;
-
 uniform Light
 {
 	vec4 ambientIntensity;
 	float lightAttenuation;
 	float maxIntensity;
-	PerLight lights[numberOfLights];
+	PerLight lights[4];
 } Lgt;
 
+uniform int numberOfLights;
 
 float CalcAttenuation(in vec3 cameraSpacePosition,
 	in vec3 cameraSpaceLightPos,
 {
 	vec4 diffuseColor = texture(diffuseColorTex, colorCoord);
 	
-	/*
 	vec4 accumLighting = diffuseColor * Lgt.ambientIntensity;
 	for(int light = 0; light < numberOfLights; light++)
 	{
 		accumLighting += ComputeLighting(diffuseColor, Lgt.lights[light]);
 	}
 	
-	accumLighting = accumLighting / Lgt.maxIntensity;
-	*/
+	outputColor = accumLighting / Lgt.maxIntensity;
 
-	outputColor = diffuseColor;
+//	outputColor = diffuseColor;
 }

File Tut 16 Gamma and Textures/tutorials.lua

 	"data/PT.vert",
 	"data/textureGamma.frag", "data/textureNoGamma.frag")
 
-SetupProject("Tut 16 Gamma Landscape", "Gamma Landscape.cpp",
+SetupProject("Tut 16 Gamma Landscape",
+	"Gamma Landscape.cpp", "LightEnv.h", "LightEnv.cpp",
 	"data/PNT.vert",
 	"data/litGamma.frag", "data/litNoGamma.frag")

File framework/Mesh.cpp

 		std::vector<std::pair<std::string, std::vector<GLuint> > > namedVaoList;
 
 		{
-			std::string strDataFilename = LOCAL_FILE_DIR + strFilename;
+			std::string strDataFilename = FindFileOrThrow(strFilename);
 			std::ifstream fileStream(strDataFilename.c_str());
 			if(!fileStream.is_open())
-			{
-				fileStream.clear();
-				strDataFilename = GLOBAL_FILE_DIR + strFilename;
-				fileStream.open(strDataFilename.c_str());
-
-				if(!fileStream.is_open())
-					throw std::runtime_error("Could not find the mesh file.");
-			}
+				throw std::runtime_error("Could not find the mesh file.");
 
 			TiXmlDocument theDoc;
 

File framework/MousePole.h

 #define FRAMEWORK_MOUSE_POLE_H
 
 #include <glm/glm.hpp>
+#include <GL/freeglut.h>
+#include <glutil/MousePoles.h>
 
 namespace Framework
 {
 		void MoveCloser(bool bLargeStep = true);
 		void MoveAway(bool bLargeStep = true);
 	};
+
+	inline int calc_glut_modifiers()
+	{
+		int ret = 0;
+
+		int modifiers = glutGetModifiers();
+		if(modifiers & GLUT_ACTIVE_SHIFT)
+			ret |= glutil::MM_KEY_SHIFT;
+		if(modifiers & GLUT_ACTIVE_CTRL)
+			ret |= glutil::MM_KEY_CTRL;
+		if(modifiers & GLUT_ACTIVE_ALT)
+			ret |= glutil::MM_KEY_ALT;
+
+		return ret;
+	}
+
+	template<typename Pole>
+	inline void ForwardMouseMotion(Pole &forward, int x, int y)
+	{
+		forward.MouseMove(glm::ivec2(x, y));
+	}
+
+	template<typename Pole>
+	inline void ForwardMouseButton(Pole &forward, int button, int state, int x, int y)
+	{
+		int modifiers = calc_glut_modifiers();
+
+		glm::ivec2 mouseLoc = glm::ivec2(x, y);
+
+		glutil::MouseButtons eButton;
+
+		switch(button)
+		{
+		case GLUT_LEFT_BUTTON:
+			eButton = glutil::MB_LEFT_BTN;
+			break;
+		case GLUT_MIDDLE_BUTTON:
+			eButton = glutil::MB_MIDDLE_BTN;
+			break;
+		case GLUT_RIGHT_BUTTON:
+			eButton = glutil::MB_RIGHT_BTN;
+			break;
+#ifdef LOAD_X11
+			//Linux Mouse wheel support
+		case 3:
+			{
+				forward.MouseWheel(1, modifiers, mouseLoc);
+				return;
+			}
+		case 4:
+			{
+				forward.MouseWheel(-1, modifiers, mouseLoc);
+				return;
+			}
+#endif
+		default:
+			return;
+		}
+
+		forward.MouseClick(eButton, state == GLUT_DOWN, modifiers, glm::ivec2(x, y));
+	}
+
+	template<typename Pole>
+	inline void ForwardMouseWheel(Pole &forward, int wheel, int direction, int x, int y)
+	{
+		forward.MouseWheel(direction, calc_glut_modifiers(), glm::ivec2(x, y));
+	}
+
 }
 
 #endif //FRAMEWORK_MOUSE_POLE_H

File framework/Timer.cpp

 		switch(m_eType)
 		{
 		case TT_LOOP:
-			return fmodf(m_secAccumTime, m_secDuration) * m_secDuration;
+			return fmodf(m_secAccumTime, m_secDuration);
 		case TT_SINGLE:
 			return glm::clamp(m_secAccumTime, 0.0f, m_secDuration);
 		}

File framework/framework.cpp

 		shaderData << shaderFile.rdbuf();
 		shaderFile.close();
 
-		return glutil::CompileShader(eShaderType, shaderData.str());
+		try
+		{
+			return glutil::CompileShader(eShaderType, shaderData.str());
+		}
+		catch(std::exception &e)
+		{
+			fprintf(stderr, e.what());
+			throw;
+		}
 	}
 
 	GLuint CreateProgram(const std::vector<GLuint> &shaderList)
 	{
-		return glutil::LinkProgram(shaderList);
+		try
+		{
+			return glutil::LinkProgram(shaderList);
+		}
+		catch(std::exception &e)
+		{
+			fprintf(stderr, e.what());
+			throw;
+		}
 	}
 
 	float DegToRad(float fAngDeg)