Commits

Jason McKesson committed ab147fd Merge

Merge with default

  • Participants
  • Parent commits 874866f, f1867b0
  • Branches 0.4
  • Tags 0.4.2

Comments (0)

Files changed (38)

 gliInterceptLog.txt
 gliLog.txt
 OpenGL32.dll
+*.workspace
+*.cbp
+*.depend
+*.layout
 6b2adf0b17eff5eeee00cda855f0e43dc4652fb1 0.4.0
 3602343eb20f7aaf7bcc9fde5b34f3de1cc57a2e 0.4.0
 8d5fd3371296b8360b2842de8278fa71fc4effb2 0.4.1
+8d5fd3371296b8360b2842de8278fa71fc4effb2 0.4.1
+f695602f9a3f4f8e569c3b416d6be1690de1aee2 0.4.1

File docs/glsdk.doxy

 
 \section page_use_premake With Premake
 
+This section assumes that you are familiar with Premake4. You should know how to set up a <tt>premake4.lua</tt> build file. You should know how to link projects to static libraries and modify include directories. In short: you should use these instructions if you are reasonably proficient with Premake4.
+
 Usually when using Premake 4, you will have a file called <tt>premake4.lua</tt> in your directory. This file may call other files to generate the solutions and projects, or it may not.
 
 Regardless of whether the main <tt>premake4.lua</tt> file does all the work, the first thing you should do is execute this instruction:

File glimg/Test/Test4.lua

 	includedirs { "../include", "../../glload/include" }
 	links {"glload", "glimg"}
 	
-	UseLibs "freeglut"
+	UseLibs {"freeglut"}
 
 	files {"test.cpp", "test.h", "main.cpp"}
 
 		flags {"OptimizeSpeed", "NoFramePointer", "ExtraWarnings", "NoEditAndContinue"};
 		objdir "Release";
 
+project "CubemapTest"
+	kind "WindowedApp"
+	language "c++"
+	includedirs { "../include", "../../glload/include" }
+	links {"glload", "glimg"}
+	
+	UseLibs {"freeglut", "glutil", "glmesh", "glm"}
+
+	files {"cubemap.cpp"}
+
+	configuration "windows"
+		defines {"WIN32"}
+		links {"glu32", "opengl32", "gdi32", "winmm", "user32"}
+
+	configuration "linux"
+		links { "GL" }
+	
+	configuration "Debug"
+		flags "Unicode";
+		defines {"DEBUG", "_DEBUG", "MEMORY_DEBUGGING"};
+		objdir "Debug";
+		flags "Symbols";
+	
+	configuration "Release"
+		defines {"NDEBUG", "RELEASE"};
+		flags "Unicode";
+		flags {"OptimizeSpeed", "NoFramePointer", "ExtraWarnings", "NoEditAndContinue"};
+		objdir "Release";
+

File glimg/Test/cubemap.cpp

+#include <vector>
+#include <string>
+#include <exception>
+#include <stdexcept>
+#include <memory>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <glload/gl_3_3.h>
+#include <glload/gll.hpp>
+#include <GL/freeglut.h>
+#include <glutil/glutil.h>
+#include <glimg/glimg.h>
+#include <glmesh/glmesh.h>
+#include <glm/glm.hpp>
+#include <glm/gtc/matrix_transform.hpp>
+#include <glm/gtc/quaternion.hpp>
+#include <glm/gtx/quaternion.hpp>
+
+
+GLuint g_texture;
+GLuint g_program;
+
+GLint g_unifModelToCameraMatrix;
+GLint g_unifCameraToClipMatrix;
+
+glmesh::Mesh *g_pSphere = NULL;
+
+void init()
+{
+	glEnable(GL_DEPTH_TEST);
+	glDepthFunc(GL_LEQUAL);
+
+	const std::string vertexShader(
+		"#version 330\n"
+		"\n"
+		"layout(location = 0) in vec4 position;\n"
+		"layout(location = 2) in vec3 normal;\n"
+		"\n"
+		"smooth out vec3 modelNormal;\n"
+		"\n"
+		"uniform mat4 cameraToClipMatrix;\n"
+		"uniform mat4 modelToCameraMatrix;\n"
+		"\n"
+		"void main()\n"
+		"{\n"
+		"	vec4 cameraPos = modelToCameraMatrix * position;\n"
+		"	gl_Position = cameraToClipMatrix * cameraPos;\n"
+		"	modelNormal = normal;\n"
+		"}\n"
+		);
+
+	const std::string fragmentShader(
+		"#version 330\n"
+		"\n"
+		"smooth in vec3 modelNormal;\n"
+		"out vec4 outputColor;\n"
+		"uniform samplerCube cubeMap;\n"
+		"\n"
+		"void main()\n"
+		"{\n"
+		"	outputColor = texture(cubeMap, modelNormal); /*vec4(((modelNormal + 1.0) / 2.0), 1.0);*/\n"
+		"}\n"
+		);
+
+	GLuint vertShader = glutil::CompileShader(GL_VERTEX_SHADER, vertexShader);
+	GLuint fragShader = glutil::CompileShader(GL_FRAGMENT_SHADER, fragmentShader);
+
+	g_program = glutil::LinkProgram(vertShader, fragShader);
+
+	glDeleteShader(vertShader);
+	glDeleteShader(fragShader);
+
+	g_unifModelToCameraMatrix = glGetUniformLocation(g_program, "modelToCameraMatrix");
+	g_unifCameraToClipMatrix = glGetUniformLocation(g_program, "cameraToClipMatrix");
+
+	GLint cubeLoc = glGetUniformLocation(g_program, "cubeMap");
+	glUseProgram(g_program);
+	glUniform1i(cubeLoc, 0);
+	glUseProgram(0);
+
+	g_pSphere = glmesh::gen::UnitSphere(6, 8);
+
+	std::auto_ptr<glimg::ImageSet> pImg(glimg::loaders::dds::LoadFromFile("pics\\cubemapdxt5test.dds"));
+	g_texture = glimg::CreateTexture(pImg.get(), 0);
+	glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
+	glBindTexture(GL_TEXTURE_CUBE_MAP, g_texture);
+	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+	glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
+}
+
+glm::ivec2 g_windowSize(0, 0);
+
+glm::vec3 g_objPos(0.0f, 3.0f, 0.0f);
+
+glutil::ViewData g_viewData = {g_objPos, glm::angleAxis(0.0f, glm::vec3(1.0f, 0.0f, 0.0f)), 20.0f, 0.0f};
+glutil::ViewScale g_viewScale = {1.0f, 50.0f, 0.5f, 0.1f, 2.0f, 0.25f, 90.0f/250.0f};
+glutil::ViewPole g_viewPole(g_viewData, g_viewScale, glutil::MB_LEFT_BTN);
+
+glutil::ObjectData g_objData = {g_objPos, glm::fquat(1.0f, 0.0f, 0.0f, 0.0f)};
+glutil::ObjectPole g_objectPole(g_objData, 90.0f/250.0f,
+								glutil::MB_RIGHT_BTN, &g_viewPole);
+
+//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()
+{
+	glEnable(GL_FRAMEBUFFER_SRGB);
+	glClearColor(0.3f, 0.5f, 0.5f, 0.5f);
+	glClearDepth(1.0f);
+	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+	glActiveTexture(GL_TEXTURE0 + 0);
+	glBindTexture(GL_TEXTURE_CUBE_MAP, g_texture);
+
+	glUseProgram(g_program);
+	glUniformMatrix4fv(g_unifCameraToClipMatrix, 1, GL_FALSE,
+		glm::value_ptr(glm::perspective(50.0f, g_windowSize.x / (float)g_windowSize.y, 1.f, 100.0f)));
+
+	glUniformMatrix4fv(g_unifModelToCameraMatrix, 1, GL_FALSE,
+		glm::value_ptr(g_viewPole.CalcMatrix() * g_objectPole.CalcMatrix()));
+
+	g_pSphere->Render("lit");
+
+	glBindTexture(GL_TEXTURE_CUBE_MAP, 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);
+	g_windowSize.x = w;
+	g_windowSize.y = h;
+}
+
+//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:
+		glutLeaveMainLoop();
+		break;
+	}
+
+	g_viewPole.CharPress(key);
+	g_objectPole.CharPress(key);
+	glutPostRedisplay();
+}
+
+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;
+}
+
+void MouseMotion(int x, int y)
+{
+	g_viewPole.MouseMove(glm::ivec2(x, y));
+	g_objectPole.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:
+		{
+			forward.MouseWheel(1, modifiers, mouseLoc);
+			return;
+		}
+	case 4:
+		{
+			forward.MouseWheel(-1, modifiers, mouseLoc);
+			return;
+		}
+#endif
+	default:
+		return;
+	}
+
+	g_viewPole.MouseClick(eButton, state == GLUT_DOWN, modifiers, glm::ivec2(x, y));
+	g_objectPole.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));
+	g_objectPole.MouseWheel(direction, calc_glut_modifiers(), glm::ivec2(x, y));
+	glutPostRedisplay();
+}
+
+int main(int argc, char** argv)
+{
+	glutInit(&argc, argv);
+
+	int width = 640;
+	int height = 480;
+	unsigned int displayMode = GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH | GLUT_STENCIL | GLUT_SRGB;
+
+	glutInitDisplayMode(displayMode);
+	glutInitContextVersion (3, 3);
+	glutInitContextProfile(GLUT_COMPATIBILITY_PROFILE);
+#ifdef DEBUG
+	glutInitContextFlags(GLUT_DEBUG);
+#endif
+	glutInitWindowSize (width, height); 
+	glutInitWindowPosition (300, 200);
+	glutCreateWindow (argv[0]);
+
+	glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION);
+
+	glload::LoadFunctions();
+	glutil::RegisterDebugOutput(glutil::STD_OUT);
+
+	init();
+
+	glutMouseFunc(MouseButton);
+	glutMotionFunc(MouseMotion);
+	glutMouseWheelFunc(MouseWheel);
+	glutDisplayFunc(display); 
+	glutReshapeFunc(reshape);
+	glutKeyboardFunc(keyboard);
+	glutMainLoop();
+	return 0;
+}

File glimg/Test/pics/cubemapdxt5test.dds

Binary file added.

File glimg/Test/pics/cubemaptest.xcf

Binary file added.

File glimg/include/glimg/DdsLoader.h

 			
 			    virtual ~DdsLoaderException() throw() {}
 
-				virtual const char *what() {return message.c_str();}
+				virtual const char *what() const throw() {return message.c_str();}
 
 			protected:
 				std::string message;
 
 			///As LoadFromFile, but from an already loaded buffer. The buffer pointer may be deleted after this call.
 			ImageSet *LoadFromMemory(const unsigned char *buffer, size_t bufSize);
-
 		}
 	}
 }

File glimg/include/glimg/ImageCreatorExceptions.h

 	public:
 	    virtual ~ImageCreationException() throw() {}
 
-		virtual const char *what() {return message.c_str();}
+		virtual const char *what() const throw() {return message.c_str();}
 
 	protected:
 		std::string message;

File glimg/include/glimg/ImageFormat.h

 
 		virtual ~InvalidFormatException() throw() {}
 
-		virtual const char *what() {return message.c_str();}
+		virtual const char *what() const throw() {return message.c_str();}
 
 	protected:
 		std::string message;

File glimg/include/glimg/StbLoader.h

 			    
 			    virtual ~StbLoaderException() throw() {}
 
-				virtual const char *what() {return message.c_str();}
+				virtual const char *what() const throw() {return message.c_str();}
 
 			protected:
 				std::string message;

File glimg/include/glimg/TextureGenerator.h

 	OpenGLPixelTransferParams GetUploadFormatType(const ImageFormat &format, unsigned int forceConvertBits);
 
 	/**
+	\brief Retrieves the texture type for the given ImageSet.
+
+	When the CreateTexture functions are called, they will create a texture with a specific
+	texture type. This is the target that the texture is bound to the context with, and this
+	information is baked into the texture object. It is vital to know what this type will actually
+	be, so that the texture object can be properly bound to the context.
+
+	This function will return the texture target that CreateTexture will create, given
+	\em exactly the same parameters as CreateTexture.
+
+	\param pImage The image that would be uploaded in a CreateTexture call.
+	\param forceConvertBits A bitfield containing values from ForcedConvertFlags.
+
+	\return The texture type for a texture that would be created with CreateTexture.
+	**/
+	GLenum GetTextureType(const ImageSet *pImage, unsigned int forceConvertBits);
+
+	/**
 	\brief Creates a texture object from the given ImageSet, with flags.
 
 	If an exception is thrown, no OpenGL state will be changed. If a texture was created with

File glimg/include/glimg/TextureGeneratorExceptions.h

 	public:
 	    virtual ~TextureGenerationException() throw() {}
 
-		virtual const char *what() {return message.c_str();}
+		virtual const char *what() const throw() {return message.c_str();}
 
 	protected:
 		std::string message;

File glimg/source/DdsLoader.cpp

 			return lineWidth * bytesPerPixel;
 		}
 
-		//Computes the offset from the first image.
-		size_t CalcByteOffsetToImage(const UncheckedImageFormat &fmt, const glimg::Dimensions &dims,
-			int mipmapLevel, int faceIx, int arrayIx)
+		size_t CalcMipmapSize( const glimg::Dimensions & dims, int currLevel, const UncheckedImageFormat &fmt)
 		{
-			//TODO: remove
-			if(faceIx != 0 || arrayIx != 0)
-				throw DdsFileUnsupportedException("", "Cubemaps/array textures not yet supported.");
+			glimg::Dimensions mipmapDims = ModifySizeForMipmap(dims, currLevel);
 
-			size_t currOffset = 0;
+			size_t lineSize = CalcLineSize(fmt, mipmapDims.width);
 
-			for(int currLevel = 0; currLevel < mipmapLevel; currLevel++)
+			int effectiveHeight = 1;
+			if(mipmapDims.numDimensions > 1)
 			{
-				glimg::Dimensions mipmapDims = ModifySizeForMipmap(dims, currLevel);
-
-				size_t lineSize = CalcLineSize(fmt, mipmapDims.width);
-
-				int effectiveHeight = 1;
-				if(mipmapDims.numDimensions > 1)
-				{
-					effectiveHeight = mipmapDims.height;
-					if(fmt.eBitdepth == BD_COMPRESSED)
-						effectiveHeight = (effectiveHeight + 3) / 4;
-				}
-
-				int effectiveDepth = 1;
-				if(mipmapDims.numDimensions > 2)
-				{
-					effectiveDepth = mipmapDims.depth;
-					if(fmt.eBitdepth == BD_COMPRESSED)
-						effectiveDepth = (effectiveDepth + 3) / 4;
-				}
-
-				int numLines = effectiveHeight * effectiveDepth;
-				currOffset += numLines * lineSize;
+				effectiveHeight = mipmapDims.height;
+				if(fmt.eBitdepth == BD_COMPRESSED)
+					effectiveHeight = (effectiveHeight + 3) / 4;
 			}
 
-			return currOffset;
+			int effectiveDepth = 1;
+			if(mipmapDims.numDimensions > 2)
+			{
+				effectiveDepth = mipmapDims.depth;
+				if(fmt.eBitdepth == BD_COMPRESSED)
+					effectiveDepth = (effectiveDepth + 3) / 4;
+			}
+
+			int numLines = effectiveHeight * effectiveDepth;
+			return numLines * lineSize;
 		}
 
 		//Takes ownership of ddsData;
 
 			const size_t baseOffset = GetByteOffsetToData(header);
 
-			std::vector<size_t> imageOffsets;
-			imageOffsets.reserve(numMipmaps * numArrays * numFaces);
-
-			//TODO: remove
-			if(numArrays != 1 || numFaces != 1)
-				throw DdsFileUnsupportedException(filename, "foo");
-
-			for(int mipmapLevel = 0; mipmapLevel < numMipmaps; mipmapLevel++)
-			{
-				size_t offsetFromFirstImg = CalcByteOffsetToImage(fmt, dims, mipmapLevel, 0, 0);
-				imageOffsets.push_back(baseOffset + offsetFromFirstImg);
-			}
-
+			//TODO: support array textures
 			//Build the image creator. No more exceptions, except for those thrown by.
 			//the ImageCreator.
 			ImageCreator imgCreator(fmt, dims, numMipmaps, numArrays, numFaces);
-			std::vector<size_t>::const_iterator it = imageOffsets.begin();
+			size_t cumulativeOffset = baseOffset;
 			for(int arrayIx = 0; arrayIx < numArrays; arrayIx++)
 			{
 				for(int faceIx = 0; faceIx < numFaces; faceIx++)
 				{
 					for(int mipmapLevel = 0; mipmapLevel < numMipmaps; mipmapLevel++)
 					{
-						imgCreator.SetImageData(&ddsData[0] + *it, true, mipmapLevel, arrayIx, faceIx);
-						++it;
+						imgCreator.SetImageData(&ddsData[0] + cumulativeOffset,
+							true, mipmapLevel, arrayIx, faceIx);
+						cumulativeOffset += CalcMipmapSize(dims, mipmapLevel, fmt);
 					}
 				}
 			}

File glimg/source/ImageCreator.cpp

 		if((faceIx < 0) || (m_faceCount <= faceIx))
 			throw FaceIndexOutOfBoundsException();
 
-		size_t imageOffset = arrayIx * faceIx * m_imageSizes[mipmapLevel];
+		size_t imageOffset = ((arrayIx * m_faceCount) + faceIx) * m_imageSizes[mipmapLevel];
 
 		unsigned char *pMipmapData = &m_imageData[mipmapLevel][0];
 		pMipmapData += imageOffset;

File glimg/source/ImageSetImpl.cpp

 
 	const void * detail::ImageSetImpl::GetImageData( int mipmapLevel, int arrayIx, int faceIx ) const
 	{
-		size_t imageOffset = arrayIx * faceIx * m_imageSizes[mipmapLevel];
+		size_t imageOffset = ((arrayIx * m_faceCount) + faceIx) * m_imageSizes[mipmapLevel];
 		return &m_imageData[mipmapLevel][0] + imageOffset;
 	}
 

File glimg/source/TextureGenerator.cpp

 						upload.format, upload.type, NULL);
 					break;
 				case 2:
+					if(texTarget == gl::GL_TEXTURE_CUBE_MAP_ARRAY)
+						arrayCount *= 6;
+
 					gl::TexImage3D(texTarget, mipmap, internalFormat, levelDims.width, levelDims.height, arrayCount,
 						0, upload.format, upload.type, NULL);
 					break;
 			return ret;
 		}
 
+		//Works for just 1D/2D/3D.
 		void TexStorageBase( GLenum texTarget, unsigned int forceConvertBits, Dimensions dims,
 			const int numMipmaps, GLuint internalFormat, const OpenGLPixelTransferParams & upload,
 			GLuint textureName )
 			if(forceConvertBits & USE_TEXTURE_STORAGE)
 				TexStorage(textureName, texTarget, dims, numMipmaps, internalFormat);
 			else
+			{
 				ManTexStorageBase(textureName, texTarget, dims, numMipmaps,
-				internalFormat, upload);
+					internalFormat, upload);
+			}
 		}
 
+		void TexStorageCube( GLenum texTarget, unsigned int forceConvertBits, Dimensions dims,
+			const int numMipmaps, GLuint internalFormat, const OpenGLPixelTransferParams & upload,
+			GLuint textureName )
+		{
+			if(forceConvertBits & USE_TEXTURE_STORAGE)
+				TexStorage(textureName, texTarget, dims, numMipmaps, internalFormat);
+			else
+			{
+				ManTexStorageCube(textureName, texTarget, dims, numMipmaps,
+					internalFormat, upload);
+			}
+		}
+
+
 		class TextureBinder
 		{
 		public:
 			unsigned int forceConvertBits, GLuint internalFormat, const OpenGLPixelTransferParams &upload)
 		{
 			ThrowIfCubeTextureNotSupported();
-			throw TextureUnexpectedException();
+
+			SetupUploadState(pImage->GetFormat(), forceConvertBits);
+			TextureBinder bind;
+			if(!(forceConvertBits & USE_DSA))
+			{
+				bind.Bind(gl::GL_TEXTURE_CUBE_MAP, textureName);
+				textureName = 0;
+			}
+
+			const int numMipmaps = pImage->GetMipmapCount();
+			TexStorageCube(gl::GL_TEXTURE_CUBE_MAP, forceConvertBits, pImage->GetDimensions(),
+				numMipmaps, internalFormat, upload, textureName);
+
+			for(int mipmap = 0; mipmap < numMipmaps; mipmap++)
+			{
+				Dimensions dims = pImage->GetDimensions(mipmap);
+
+				for(int faceIx = 0; faceIx < 6; ++faceIx)
+				{
+					const void *pPixelData = pImage->GetImageData(mipmap, 0, faceIx);
+
+					TexSubImage(textureName, gl::GL_TEXTURE_CUBE_MAP_POSITIVE_X + faceIx,
+						mipmap, internalFormat, dims, upload,
+						pPixelData, pImage->GetImageByteSize(mipmap));
+				}
+			}
+
+			FinalizeTexture(textureName, gl::GL_TEXTURE_CUBE_MAP, pImage);
 		}
 
 		void Build2DTexture(unsigned int textureName, const detail::ImageSetImpl *pImage,
 		}
 	}
 
+	GLenum GetTextureType( const ImageSet *pImage, unsigned int forceConvertBits )
+	{
+		Dimensions dims = pImage->GetDimensions();
+
+		switch(dims.numDimensions)
+		{
+		case 1:
+			//May be 1D or 1D array.
+			if(IsArrayTexture(pImage, forceConvertBits))
+				return gl::GL_TEXTURE_1D_ARRAY;
+			else
+				return gl::GL_TEXTURE_1D;
+		case 2:
+			//2D, 2D array, 2D cube, or 2D array cube.
+			if(IsArrayTexture(pImage, forceConvertBits))
+			{
+				if(pImage->GetFaceCount() > 1)
+					return gl::GL_TEXTURE_CUBE_MAP_ARRAY;
+				else
+					return gl::GL_TEXTURE_2D_ARRAY;
+			}
+			else
+			{
+				if(pImage->GetFaceCount() > 1)
+					return gl::GL_TEXTURE_CUBE_MAP;
+				else
+					return gl::GL_TEXTURE_2D;
+			}
+			break;
+		case 3:
+			//3D.
+			return gl::GL_TEXTURE_3D;
+		}
+
+		return -1;
+	}
+
 	unsigned int CreateTexture( const ImageSet *pImage, unsigned int forceConvertBits )
 	{
 		GLuint textureName = 0;
 		GLuint internalFormat = GetInternalFormat(format, forceConvertBits);
 		OpenGLPixelTransferParams upload = GetUploadFormatType(format, forceConvertBits);
 
-		Dimensions dims = pImage->GetDimensions();
-
-		switch(dims.numDimensions)
+		switch(GetTextureType(pImage, forceConvertBits))
 		{
-		case 1:
-			//May be 1D or 1D array.
-			if(IsArrayTexture(pImage, forceConvertBits))
-				Build1DArrayTexture(textureName, pImage->m_pImpl, forceConvertBits,
-					internalFormat, upload);
-			else
-				Build1DTexture(textureName, pImage->m_pImpl, forceConvertBits,
-					internalFormat, upload);
+		case gl::GL_TEXTURE_1D:
+			Build1DTexture(textureName, pImage->m_pImpl, forceConvertBits,
+				internalFormat, upload);
 			break;
-		case 2:
-			//2D, 2D array, 2D cube, or 2D array cube.
-			if(IsArrayTexture(pImage, forceConvertBits))
-			{
-				if(pImage->GetFaceCount() > 1)
-					Build2DCubeArrayTexture(textureName, pImage->m_pImpl, forceConvertBits,
-						internalFormat, upload);
-				else
-					Build2DArrayTexture(textureName, pImage->m_pImpl, forceConvertBits,
-						internalFormat, upload);
-			}
-			else
-			{
-				if(pImage->GetFaceCount() > 1)
-					Build2DCubeTexture(textureName, pImage->m_pImpl, forceConvertBits,
-						internalFormat, upload);
-				else
-					Build2DTexture(textureName, pImage->m_pImpl, forceConvertBits,
-						internalFormat, upload);
-			}
+		case gl::GL_TEXTURE_2D:
+			Build2DTexture(textureName, pImage->m_pImpl, forceConvertBits,
+				internalFormat, upload);
 			break;
-		case 3:
-			//3D.
-			Build3DTexture(textureName, pImage->m_pImpl, forceConvertBits, internalFormat, upload);
+		case gl::GL_TEXTURE_3D:
+			Build3DTexture(textureName, pImage->m_pImpl, forceConvertBits,
+				internalFormat, upload);
+			break;
+		case gl::GL_TEXTURE_1D_ARRAY:
+			Build1DArrayTexture(textureName, pImage->m_pImpl, forceConvertBits,
+				internalFormat, upload);
+			break;
+		case gl::GL_TEXTURE_2D_ARRAY:
+			Build2DArrayTexture(textureName, pImage->m_pImpl, forceConvertBits,
+				internalFormat, upload);
+			break;
+		case gl::GL_TEXTURE_CUBE_MAP:
+			Build2DCubeTexture(textureName, pImage->m_pImpl, forceConvertBits,
+				internalFormat, upload);
+			break;
+		case gl::GL_TEXTURE_CUBE_MAP_ARRAY:
+			Build2DCubeArrayTexture(textureName, pImage->m_pImpl, forceConvertBits,
+				internalFormat, upload);
 			break;
 		}
 	}

File glmesh/Test/draw_draw_test.cpp

 	virtual void Draw(const glm::mat4 &cameraToClip, const glm::mat4 &worldToCamera,
 		const glm::mat4 &modelToWorld)
 	{
+		gl::ClearColor(0.9f, 0.9f, 0.9f, 1.0f);
+		gl::ClearDepth(1.0f);
 		gl::Clear(gl::GL_COLOR_BUFFER_BIT | gl::GL_DEPTH_BUFFER_BIT);
 		
 		//Set uniforms.

File glmesh/Test/draw_mesh.cpp

+
+#include <string>
+#include <exception>
+#include <stdexcept>
+#include <stdio.h>
+#include <stdlib.h>
+#include <utility>
+#include <memory>
+
+#include <glload/gl_3_3.hpp>
+#include <glload/gll.hpp>
+#include <GL/glfw.h>
+#include <glm/glm.hpp>
+#include <glm/gtc/type_ptr.hpp>
+#include <glm/gtc/half_float.hpp>
+#include <glm/gtc/matrix_transform.hpp>
+#include <glm/gtc/quaternion.hpp>
+#include <glm/gtx/quaternion.hpp>
+#include <glutil/glutil.h>
+#include <glmesh/glmesh.h>
+#include "framework_draw.h"
+
+namespace
+{
+}
+
+class BasicDrawable : public Drawable
+{
+public:
+	BasicDrawable()
+	{
+		gl::ClearColor(1.0f, 1.0f, 1.0f, 1.0f);
+		gl::ClearDepth(1.0);
+
+		gl::Enable(gl::GL_DEPTH_TEST);
+		gl::DepthFunc(gl::GL_LEQUAL);
+		gl::Enable(gl::GL_DEPTH_CLAMP);
+
+		const std::string vertexShader(
+			"#version 330\n"
+			"\n"
+			"layout(location = 0) in vec4 position;\n"
+			"layout(location = 1) in vec4 color;\n"
+			"\n"
+			"smooth out vec4 theColor;\n"
+			"\n"
+			"uniform mat4 cameraToClipMatrix;\n"
+			"uniform mat4 modelToCameraMatrix;\n"
+			"\n"
+			"void main()\n"
+			"{\n"
+			"	vec4 cameraPos = modelToCameraMatrix * position;\n"
+			"	gl_Position = cameraToClipMatrix * cameraPos;\n"
+			"	theColor = color;\n"
+			"}\n"
+			);
+
+		const std::string fragmentShader(
+			"#version 330\n"
+			"\n"
+			"smooth in vec4 theColor;\n"
+			"out vec4 outputColor;\n"
+			"\n"
+			"void main()\n"
+			"{\n"
+			"	outputColor = theColor;\n"
+			"}\n"
+			);
+
+		GLfloat groundPlaneData[] =
+		{
+			30.0f, 0.0f, 30.0f, 1.0f,
+			30.0f, 0.0f, -30.0f, 1.0f,
+			-30.0f, 0.0f, 30.0f, 1.0f,
+			-30.0f, 0.0f, -30.0f, 1.0f,
+
+			0.2f, 1.0f, 0.2f, 1.0f,
+			0.9f, 0.5f, 0.3f, 1.0f,
+			0.2f, 1.0f, 0.2f, 1.0f,
+			0.9f, 0.5f, 0.3f, 1.0f,
+		};
+
+		GLuint vertShader = glutil::CompileShader(gl::GL_VERTEX_SHADER, vertexShader);
+		GLuint fragShader = glutil::CompileShader(gl::GL_FRAGMENT_SHADER, fragmentShader);
+
+		m_program = glutil::LinkProgram(vertShader, fragShader);
+
+		gl::DeleteShader(vertShader);
+		gl::DeleteShader(fragShader);
+
+		m_unifModelToCameraMatrix = gl::GetUniformLocation(m_program, "modelToCameraMatrix");
+		m_unifCameraToClipMatrix = gl::GetUniformLocation(m_program, "cameraToClipMatrix");
+
+		std::vector<GLuint> buffers(1);
+		gl::GenBuffers(1, &buffers[0]);
+
+		gl::BindBuffer(gl::GL_ARRAY_BUFFER, buffers[0]);
+		gl::BufferData(gl::GL_ARRAY_BUFFER, sizeof(groundPlaneData), groundPlaneData, gl::GL_STATIC_DRAW);
+
+		GLuint mainVao = 0;
+		gl::GenVertexArrays(1, &mainVao);
+
+		gl::BindVertexArray(mainVao);
+		gl::EnableVertexAttribArray(0);
+		gl::VertexAttribPointer(0, 4, gl::GL_FLOAT, gl::GL_FALSE, 0, 0);
+		gl::EnableVertexAttribArray(1);
+		gl::VertexAttribPointer(1, 4, gl::GL_FLOAT, gl::GL_FALSE, 0,
+			reinterpret_cast<void*>(16 * sizeof(GLfloat)));
+
+		gl::BindVertexArray(0);
+
+		glmesh::RenderCmdList cmdList;
+		cmdList.DrawArrays(gl::GL_TRIANGLE_STRIP, 0, 4);
+
+		glmesh::MeshVariantMap variants;
+		glmesh::MeshVariantMap::value_type testVar("test", 0);
+		gl::GenVertexArrays(1, &testVar.second);
+
+		gl::BindVertexArray(testVar.second);
+		gl::EnableVertexAttribArray(0);
+		gl::VertexAttribPointer(0, 4, gl::GL_FLOAT, gl::GL_FALSE, 0, 0);
+
+		gl::BindVertexArray(0);
+		
+		gl::BindBuffer(gl::GL_ARRAY_BUFFER, 0);
+
+		variants.insert(testVar);
+		variants["main"] = mainVao;
+
+		m_pGroundPlane.reset(new glmesh::Mesh(buffers, mainVao, cmdList, variants));
+	}
+
+	~BasicDrawable()
+	{
+		gl::DeleteProgram(m_program);
+	}
+
+	virtual void Draw(const glm::mat4 &cameraToClip, const glm::mat4 &worldToCamera,
+		const glm::mat4 &modelToWorld)
+	{
+		gl::ClearColor(0.9f, 0.9f, 0.9f, 1.0f);
+		gl::ClearDepth(1.0f);
+		gl::Clear(gl::GL_COLOR_BUFFER_BIT | gl::GL_DEPTH_BUFFER_BIT);
+
+		//Set uniforms.
+		gl::UseProgram(m_program);
+		gl::UniformMatrix4fv(m_unifCameraToClipMatrix, 1, gl::GL_FALSE,
+			glm::value_ptr(cameraToClip));
+
+		{
+			gl::UniformMatrix4fv(m_unifModelToCameraMatrix, 1, gl::GL_FALSE,
+				glm::value_ptr(worldToCamera));
+
+			m_pGroundPlane->Render("main");
+		}
+
+		gl::UseProgram(0);
+	}
+
+private:
+	GLuint m_program;
+	GLuint m_unifModelToCameraMatrix;
+	GLuint m_unifCameraToClipMatrix;
+
+	std::auto_ptr<glmesh::Mesh> m_pGroundPlane;
+};
+
+
+Drawable *CreateDrawable()
+{
+	return new BasicDrawable();
+}

File glmesh/Test/draw_sphere.cpp

+
+#include <string>
+#include <exception>
+#include <stdexcept>
+#include <stdio.h>
+#include <stdlib.h>
+#include <utility>
+#include <memory>
+
+#include <glload/gl_3_3.hpp>
+#include <glload/gll.hpp>
+#include <GL/glfw.h>
+#include <glm/glm.hpp>
+#include <glm/gtc/type_ptr.hpp>
+#include <glm/gtc/half_float.hpp>
+#include <glm/gtc/matrix_transform.hpp>
+#include <glm/gtc/quaternion.hpp>
+#include <glm/gtx/quaternion.hpp>
+#include <glutil/glutil.h>
+#include <glmesh/glmesh.h>
+#include "framework_draw.h"
+
+namespace
+{
+	struct ProgramData
+	{
+		GLuint program;
+		GLuint unifModelToCameraMatrix;
+		GLuint unifCameraToClipMatrix;
+
+		ProgramData()
+			: program(0)
+		{}
+
+		~ProgramData()
+		{
+			if(program)
+				gl::DeleteProgram(program);
+		}
+
+		void Load(const std::string &vertexShader, const std::string &fragmentShader)
+		{
+			try
+			{
+				GLuint vertShader = glutil::CompileShader(gl::GL_VERTEX_SHADER, vertexShader);
+				GLuint fragShader = glutil::CompileShader(gl::GL_FRAGMENT_SHADER, fragmentShader);
+
+				program = glutil::LinkProgram(vertShader, fragShader);
+
+				gl::DeleteShader(vertShader);
+				gl::DeleteShader(fragShader);
+			}
+			catch(glutil::ShaderException &e)
+			{
+				printf("%s\n", e.what());
+
+				throw;
+			}
+
+			unifModelToCameraMatrix = gl::GetUniformLocation(program, "modelToCameraMatrix");
+			unifCameraToClipMatrix = gl::GetUniformLocation(program, "cameraToClipMatrix");
+		}
+	};
+}
+
+class BasicDrawable : public Drawable
+{
+public:
+	BasicDrawable()
+	{
+		gl::ClearColor(1.0f, 1.0f, 1.0f, 1.0f);
+		gl::ClearDepth(1.0);
+
+		gl::Enable(gl::GL_DEPTH_TEST);
+		gl::DepthFunc(gl::GL_LEQUAL);
+		gl::Enable(gl::GL_DEPTH_CLAMP);
+
+		gl::Enable(gl::GL_CULL_FACE);
+
+		const std::string groundVertexShader(
+			"#version 330\n"
+			"\n"
+			"layout(location = 0) in vec4 position;\n"
+			"layout(location = 1) in vec4 color;\n"
+			"\n"
+			"smooth out vec4 theColor;\n"
+			"\n"
+			"uniform mat4 cameraToClipMatrix;\n"
+			"uniform mat4 modelToCameraMatrix;\n"
+			"\n"
+			"void main()\n"
+			"{\n"
+			"	vec4 cameraPos = modelToCameraMatrix * position;\n"
+			"	gl_Position = cameraToClipMatrix * cameraPos;\n"
+			"	theColor = color;\n"
+			"}\n"
+			);
+
+		const std::string groundFragmentShader(
+			"#version 330\n"
+			"\n"
+			"smooth in vec4 theColor;\n"
+			"out vec4 outputColor;\n"
+			"\n"
+			"void main()\n"
+			"{\n"
+			"	outputColor = theColor;\n"
+			"}\n"
+			);
+
+		m_ground.Load(groundVertexShader, groundFragmentShader);
+
+		const std::string objVertexShader(
+			"#version 330\n"
+			"\n"
+			"layout(location = 0) in vec4 position;\n"
+			"layout(location = 2) in vec3 normal;\n"
+			"layout(location = 5) in vec2 texCoord;\n"
+			"\n"
+			"smooth out vec3 modelNormal;\n"
+			"smooth out vec2 textureCoordinates;\n"
+			"\n"
+			"uniform mat4 cameraToClipMatrix;\n"
+			"uniform mat4 modelToCameraMatrix;\n"
+			"\n"
+			"void main()\n"
+			"{\n"
+			"	vec4 cameraPos = modelToCameraMatrix * position;\n"
+			"	gl_Position = cameraToClipMatrix * cameraPos;\n"
+			"	modelNormal = normal;\n"
+			"   textureCoordinates = texCoord;\n"
+			"}\n"
+			);
+
+		const std::string objFragmentShader(
+			"#version 330\n"
+			"\n"
+			"smooth in vec3 modelNormal;\n"
+			"smooth in vec2 textureCoordinates;\n"
+			"out vec4 outputColor;\n"
+			"\n"
+			"void main()\n"
+			"{\n"
+			"	outputColor = vec4(modelNormal, 1.0);\n"
+			"}\n"
+			);
+
+		m_object.Load(objVertexShader, objFragmentShader);
+
+		GLfloat groundPlaneData[] =
+		{
+			30.0f, 0.0f, 30.0f, 1.0f,
+			30.0f, 0.0f, -30.0f, 1.0f,
+			-30.0f, 0.0f, 30.0f, 1.0f,
+			-30.0f, 0.0f, -30.0f, 1.0f,
+
+			0.2f, 1.0f, 0.2f, 1.0f,
+			0.9f, 0.5f, 0.3f, 1.0f,
+			0.2f, 1.0f, 0.2f, 1.0f,
+			0.9f, 0.5f, 0.3f, 1.0f,
+		};
+
+		std::vector<GLuint> buffers(1);
+		gl::GenBuffers(1, &buffers[0]);
+
+		gl::BindBuffer(gl::GL_ARRAY_BUFFER, buffers[0]);
+		gl::BufferData(gl::GL_ARRAY_BUFFER, sizeof(groundPlaneData), groundPlaneData, gl::GL_STATIC_DRAW);
+
+		GLuint mainVao = 0;
+		gl::GenVertexArrays(1, &mainVao);
+
+		gl::BindVertexArray(mainVao);
+		gl::EnableVertexAttribArray(0);
+		gl::VertexAttribPointer(0, 4, gl::GL_FLOAT, gl::GL_FALSE, 0, 0);
+		gl::EnableVertexAttribArray(1);
+		gl::VertexAttribPointer(1, 4, gl::GL_FLOAT, gl::GL_FALSE, 0,
+			reinterpret_cast<void*>(16 * sizeof(GLfloat)));
+
+		gl::BindVertexArray(0);
+
+		glmesh::RenderCmdList cmdList;
+		cmdList.DrawArrays(gl::GL_TRIANGLE_STRIP, 0, 4);
+
+		glmesh::MeshVariantMap variants;
+		glmesh::MeshVariantMap::value_type testVar("test", 0);
+		gl::GenVertexArrays(1, &testVar.second);
+
+		gl::BindVertexArray(testVar.second);
+		gl::EnableVertexAttribArray(0);
+		gl::VertexAttribPointer(0, 4, gl::GL_FLOAT, gl::GL_FALSE, 0, 0);
+
+		gl::BindVertexArray(0);
+
+		gl::BindBuffer(gl::GL_ARRAY_BUFFER, 0);
+
+		variants.insert(testVar);
+
+		m_pGroundPlane.reset(new glmesh::Mesh(buffers, mainVao, cmdList, variants));
+
+		m_pSphere.reset(glmesh::gen::UnitSphere(6, 8));
+	}
+
+	~BasicDrawable()
+	{}
+
+	virtual void Draw(const glm::mat4 &cameraToClip, const glm::mat4 &worldToCamera,
+		const glm::mat4 &modelToWorld)
+	{
+		gl::ClearColor(0.9f, 0.9f, 0.9f, 1.0f);
+		gl::ClearDepth(1.0f);
+		gl::Clear(gl::GL_COLOR_BUFFER_BIT | gl::GL_DEPTH_BUFFER_BIT);
+
+		//Set uniforms.
+		gl::UseProgram(m_ground.program);
+		gl::UniformMatrix4fv(m_ground.unifCameraToClipMatrix, 1, gl::GL_FALSE,
+			glm::value_ptr(cameraToClip));
+
+		{
+			gl::UniformMatrix4fv(m_ground.unifModelToCameraMatrix, 1, gl::GL_FALSE,
+				glm::value_ptr(worldToCamera));
+
+			m_pGroundPlane->Render();
+		}
+
+		gl::UseProgram(m_object.program);
+		gl::UniformMatrix4fv(m_object.unifCameraToClipMatrix, 1, gl::GL_FALSE,
+			glm::value_ptr(cameraToClip));
+
+		{
+			gl::UniformMatrix4fv(m_object.unifModelToCameraMatrix, 1, gl::GL_FALSE,
+				glm::value_ptr(worldToCamera * modelToWorld));
+
+			m_pSphere->Render();
+		}
+
+		gl::UseProgram(0);
+	}
+
+private:
+	ProgramData m_ground;
+	ProgramData m_object;
+
+	std::auto_ptr<glmesh::Mesh> m_pGroundPlane;
+	std::auto_ptr<glmesh::Mesh> m_pSphere;
+};
+
+
+Drawable *CreateDrawable()
+{
+	return new BasicDrawable();
+}

File glmesh/Test/draw_streambuffer_test.cpp

 	virtual void Draw(const glm::mat4 &cameraToClip, const glm::mat4 &worldToCamera,
 		const glm::mat4 &modelToWorld)
 	{
-
+		gl::ClearColor(0.9f, 0.9f, 0.9f, 1.0f);
+		gl::ClearDepth(1.0f);
 		gl::Clear(gl::GL_COLOR_BUFFER_BIT | gl::GL_DEPTH_BUFFER_BIT);
 		
 		//Update data in stream buffer.

File glmesh/Test/draw_vertexformat_test.cpp

 	virtual void Draw(const glm::mat4 &cameraToClip, const glm::mat4 &worldToCamera,
 		const glm::mat4 &modelToWorld)
 	{
+		gl::ClearColor(0.9f, 0.9f, 0.9f, 1.0f);
+		gl::ClearDepth(1.0f);
 		gl::Clear(gl::GL_COLOR_BUFFER_BIT | gl::GL_DEPTH_BUFFER_BIT);
 		
 		//Update data in stream buffer.

File glmesh/glmesh.doxy

 
 Before rendering can begin, some setup work needs to be done. Core OpenGL 3.1 and above can only render from buffer objects. Therefore, this drawing API must use a buffer object.
 
-This is provided by the StreamBuffer class. It provides an interface for sequentially adding vertex data to a buffer. It can invalidate the buffer automatically once the sequential access if there is not enough room to write a particular set of vertex data.
+This is provided by the StreamBuffer class. It provides an interface for sequentially adding vertex data to a buffer. It can invalidate the buffer automatically if there is not enough room left in the buffer to write a particular set of vertex data.
 
 The next setup is to declare a format for the vertex data. Unlike the original immediate mode, the drawing API requires an explicit format for vertex data. This is provided by the VertexFormat class, which defines a series of vertex attributes.
 
 **/
 
 /**
+\defgroup module_glmesh_mesh GL Static Mesh
+\ingroup module_glmesh
+
+**/
+
+/**
+\defgroup module_glmesh_mesh_generator Mesh Generators
+\ingroup module_glmesh_mesh
+
+All generated meshes use the OpenGL default for their face orientation. Namely, counter-clockwise is front. The model space meshes are all in a right-handed coordinate system, as is standard for OpenGL.
+
+The attribute indices used by the mesh generator functions are standardized. They are as follows:
+
+\li 0: Position (three-dimensional, float)
+\li 1: Color, diffuse (four-dimensional, float)
+\li 2: Normal (three-dimensional, float)
+\li 3: Tangent, for establishing an NBT basis (three-dimensional, float)
+\li 4: Binormal, for establishing an NBT basis (three-dimensional, float)
+\li 5: Texture coordinate (two-dimensional, float)
+
+The main VAO of each mesh includes all attributes. There will be variants generated containing fewer attributes. The naming convention for generated mesh variants is as follows:
+
+\li <tt>unlit</tt> or <tt>flat</tt>: Position
+\li <tt>lit</tt>: Position+Normal
+\li <tt>tex</tt>: Position+TexCoord
+\li <tt>color</tt>: Position+Color
+
+These can be combined with <tt>-</tt> characters (except for <tt>unlit</tt>). So <tt>lit-tex</tt> means positions, normals, and texture coordinates. The order doesn't matter.
+
+**/
+
+/**
 \defgroup module_glmesh_exceptions GL Mesh Exceptions
 \ingroup module_glmesh
 

File glmesh/include/glmesh/Draw.h

 	public:
 		virtual ~DrawException() throw() {}
 
-		virtual const char *what() {return message.c_str();}
+		virtual const char *what() const throw() {return message.c_str();}
 
 	protected:
 		std::string message;
 	The Attrib specialization must match the type of the attribute from the VertexFormat \em exactly.
 	The number of components however does not have to match.
 
+	Successfully constructing an object of this type will affect the following OpenGL state (note: none
+	of this state will be touched in the event of an exception):
+
+	\li The current GL_ARRAY_BUFFER binding.
+	\li If VAOs are available, the current VAO will be affected. The current VAO after this object is
+	destroyed will be VAO 0. The VAO used to render will be the one stored in the StreamBuffer.
+	\li If VAOs are not available, the current attribute array state will be modified as
+	VertexFormat::Enable does. Note that you should make sure that all attributes are disabled
+	*before* rendering with this immediate mode. Otherwise, badness can result.
+
+	\note Do \em not attempt to change buffer binding state while an instance of this class is constructed.
+	Also, do not attempt to create two of these objects at the same time.
+
 	\todo The 1.0 for the fourth value doesn't work right with normalized values.
 
 	\note This class cannot be copied.

File glmesh/include/glmesh/GenDescriptors.h

+
+#ifndef GLSDK_MESH_GENERATOR_DESCRIPTORS_H
+#define GLSDK_MESH_GENERATOR_DESCRIPTORS_H
+
+/**
+\file
+\brief Describes the mesh generator flags.
+
+**/
+
+namespace glmesh
+{
+}
+
+
+
+
+#endif //GLSDK_MESH_GENERATOR_DESCRIPTORS_H

File glmesh/include/glmesh/Mesh.h

+
+#ifndef GLSDK_MESH_MESH_H
+#define GLSDK_MESH_MESH_H
+
+/**
+\file
+\brief Defines the Mesh class and associated types for creating a Mesh. Include an OpenGL header before including this one.
+**/
+
+#include <memory>
+#include <vector>
+#include <map>
+#include <string>
+
+namespace glmesh
+{
+	///\addtogroup module_glmesh_mesh
+	///@{
+
+	///A list of variations of named variations of a mesh. Each name maps to a VAO to render from.
+	typedef std::map<std::string, GLuint> MeshVariantMap;
+
+	struct RenderCmdListData;
+
+	/**
+	\brief A list of rendering commands to be used by a Mesh.
+	
+	This class is part of the setup process for creating a Mesh. It stores a series of OpenGL
+	rendering commands which will be used when rendering a particular Mesh. The series of commands
+	will be used in the order given.
+
+	Indexed rendering commands (DrawElements and its ilk) will use the element buffer that is
+	stored in the VAO used by the Mesh to render.
+
+	The rendering command functions mimic their OpenGL counterparts where possible.
+	**/
+	class RenderCmdList
+	{
+	public:
+		///Creates an empty RenderCmdList.
+		RenderCmdList();
+		///Destroys the object.
+		~RenderCmdList();
+
+		RenderCmdList(class RenderCmdList &anOther);
+		RenderCmdList& operator=(class RenderCmdList &anOther);
+
+		/**
+		\brief Adds a glDrawArrays command to the list of rendering commands.
+		
+		\param primitive The OpenGL primitive type to render.
+		\param startIndex The first index in the vertex arrays to render.
+		\param vertexCount The number of vertices to render.
+		**/
+		void DrawArrays(GLenum primitive, GLint startIndex, GLsizei vertexCount);
+
+		/**
+		\brief Adds a glDrawElements-style command to the list of rendering commands.
+
+		If there is a currently set restart index (with PrimitiveRestartIndex(GLuint) ), then
+		it will be rendered with that as the restart index.
+		
+		\param primitive The OpenGL primitive type to render.
+		\param vertexCount The number of vertices to render.
+		\param dataType The OpenGL data type of the indices (GL_UNSIGNED_SHORT, etc).
+		\param byteOffset The offset from the beginning of the index buffer to where OpenGL will begin to pull data.
+		\param baseVertex An integer offset to be added to each index before fetching from the buffer. The restart
+		index check happens before adding \a baseVertex to the index.
+		**/
+		void DrawElements(GLenum primitive, GLsizei vertexCount, GLenum dataType, GLintptr byteOffset,
+			GLint baseVertex = 0);
+
+		///Subsequent indexed rendering functions will not use a primitive restart index.
+		void PrimitiveRestartIndex();
+
+		/**
+		\brief Subsequent indexed rendering functions will use the given index as the primitive restart index.
+
+		The primitive restart index will affect all indexed rendering functions (like DrawElements) until
+		it is changed with a later call to this function. To stop using the restart index,
+		use PrimitiveRestartIndex().
+		**/
+		void PrimitiveRestartIndex(GLuint index);
+
+	private:
+		std::auto_ptr<RenderCmdListData> m_pData;
+
+		friend class Mesh;
+	};
+
+
+	struct MeshData;
+
+	/**
+	\brief An object that represents a static collection of mesh data.
+
+	Mesh objects are created to represent static data, where Draw and its ilk are used for more dynamic
+	or simple objects.
+
+	Mesh objects are composed of a VAO (which sets up all of the vertex attribute commands),
+	coupled with a series of rendering commands, provided by a RenderCmdList.
+	
+	A Mesh can have multiple VAOs, each of which has a unique name. These named VAOs
+	are called variants. The main VAO, which is unnamed, . All of the variants are rendered
+	with the same sequence of rendering commands.
+
+	This object owns the VAOs it is provided with, and it will delete them. It can also be given
+	ownership of one or more buffer objects, which it will also delete when it is deleted. It does not
+	use these buffer objects for any purpose other than to delete them.
+	
+	This class cannot be copied.
+
+	Rendering with this class (via any call to Render) will affect the following OpenGL state:
+
+	\li The current VAO binding. After the call, it will be reset to 0.
+	\li The current GL_PRIMITIVE_RESTART enable. After the call, it will be disabled.
+
+	\note: This class requires the presence of ARB_vertex_array_object or OpenGL 3.0 or above.
+
+	\note: You must ensure that the OpenGL context exists when this Mesh's is called.
+	**/
+	class Mesh
+	{
+	public:
+		/**
+		\brief Creates a mesh, given a series of buffer objects, VAOs, and rendering commands.
+
+		This class claims ownership of \em all OpenGL objects passed to it. Its destructor will delete
+		every VAO and buffer object it is given. If you want to manage the lifetime of a buffer object
+		elsewhere, do not pass it as a parameter. Your VAOs can still reference those buffers,
+		but this class will not dig through VAOs to delete buffers.
+
+		\param bufferObjects A series of buffer objects to be freed when this Mesh is destroyed.
+		The same buffer object \em cannot be in this array multiple times.
+		\param mainVao The primary vertex array object, used when calling Render with no parameters.
+		If it is 0, then the only way to render the mesh is with a named variant.
+		\param renderCmds The series of rendering commands to be used to render this object. This sequence
+		will be used for every variant.
+		\param variants A std::map of named variants for the Mesh. It is legal to use the same VAOs for
+		the \a mainVao and one of the variants, or between different variants.
+		**/
+		Mesh(const std::vector<GLuint> &bufferObjects, GLuint mainVao, const RenderCmdList &renderCmds,
+			const MeshVariantMap &variants = MeshVariantMap());
+
+		///Deletes all VAOs and buffer objects owned by this Mesh. The OpenGL context must still exist.
+		~Mesh();
+
+		///Renders the main VAO. Does nothing if there is no main VAO.
+		void Render() const;
+
+		///Renders a named variant. Does nothing if the named VAO does not exist.
+		void Render(const std::string &variantName) const;
+
+		///Returns true if there is a main VAO.
+		bool DoesMainExist() const;
+
+		///Returns true if the named variant exists.
+		bool DoesVariantExist(const std::string &variantName) const;
+
+	private:
+		std::auto_ptr<MeshData> m_pData;
+
+		Mesh(const Mesh&);
+		Mesh &operator=(const Mesh&);
+
+		void RenderWithCurrVao() const;
+	};
+
+	///@}
+}
+
+
+
+#endif //GLSDK_MESH_MESH_H

File glmesh/include/glmesh/Quadrics.h

+
+#ifndef GLSDK_MESH_QUADRICS_H
+#define GLSDK_MESH_QUADRICS_H
+
+/**
+\file
+\brief Defines mesh generators for various quadrics and curved surfaces.
+**/
+
+#include "GenDescriptors.h"
+
+namespace glmesh
+{
+	class Mesh;
+
+	namespace gen
+	{
+		///\addtogroup module_glmesh_mesh_generator
+		///@{
+
+		/**
+		\brief Creates a unit sphere at the origin.
+
+		A unit sphere is a sphere of radius 1, so it extends from [-1, 1] in all three axes.
+		
+		**/
+		Mesh *UnitSphere(int numHorizSlices, int numVertSlices);
+
+		///@}
+	}
+}
+
+
+
+#endif //GLSDK_MESH_QUADRICS_H

File glmesh/include/glmesh/StreamBuffer.h

 	public:
 		virtual ~StreamBufferException() throw() {}
 
-		virtual const char *what() {return message.c_str();}
+		virtual const char *what() const throw() {return message.c_str();}
 
 	protected:
 		std::string message;

File glmesh/include/glmesh/VertexFormat.h

 	public:
 		virtual ~VertexFormatException() throw() {}
 
-		virtual const char *what() {return message.c_str();}
+		virtual const char *what() const throw() {return message.c_str();}
 
 	protected:
 		std::string message;
 		\ingroup module_glmesh_draw
 		\brief RAII-style class for binding a VertexFormat to the OpenGL context. The destructor unbinds it.
 
-		This class assumes that a valid VAO is bound (if one is needed), as well as that
+		This class assumes that a valid VAO is bound (if one is needed). It also assumes that
 		all vertex data comes from a single buffer object which has also been bound to GL_ARRAY_BUFFER.
 
+		The following OpenGL state is touched by constructing this object:
+
+		\li For each attribute in the given VertexFormat, that attributes state will be changed. After this
+		object is destroyed, all of the attributes used by this VertexFormat will be disabled.
+
 		After creating one of these, you can use \c glDraw* functions to render from the previously
 		bound buffer object, using the VertexFormat given.
 		**/
 			this before this object is destroyed.
 			\param baseOffset The byte offset from the beginning of the buffer to the first piece of
 			vertex data.
-
 			**/
 			Enable(const VertexFormat &fmt, size_t baseOffset);
 

File glmesh/include/glmesh/glmesh.h

 #include "StreamBuffer.h"
 #include "VertexFormat.h"
 #include "Draw.h"
+#include "Mesh.h"
+#include "GenDescriptors.h"
+#include "Quadrics.h"
 
 
 /**

File glmesh/source/GenHelper.cpp

+
+#include <vector>
+#include <algorithm>
+#include <sstream>
+#include <glload/gl_all.hpp>
+#include "GenHelper.h"
+
+#define ARRAY_COUNT( array ) (sizeof( array ) / (sizeof( array[0] ) * (sizeof( array ) != sizeof(void*) || sizeof( array[0] ) <= sizeof(void*))))
+
+namespace glmesh
+{
+	namespace gen
+	{
+		namespace
+		{
+			const char *g_variantNames[] =
+			{
+				"color",
+				"lit",
+				"tex",
+			};
+
+			void CalcName(std::ostringstream &outStream, const std::vector<std::string> &nameList)
+			{
+				outStream.clear();
+				outStream.str("");
+				outStream << nameList[0];
+				for(size_t loop = 1; loop < nameList.size(); ++loop)
+					outStream << "-" << nameList[loop];
+			}
+
+			std::vector<std::string> GenerateNameList(const int components)
+			{
+				std::vector<std::string> currNames;
+				currNames.reserve(4);
+
+				for(size_t comp = 0; comp < ARRAY_COUNT(g_variantNames); ++comp)
+				{
+					if(components & (0x1 << comp))
+						currNames.push_back(g_variantNames[comp]);
+				}
+
+				return currNames;
+			}
+		}
+
+		std::string GenerateNameForVariant( const int components )
+		{
+			if(components == 0)
+				return "flat";
+
+			std::ostringstream theName;
+			CalcName(theName, GenerateNameList(components));
+
+			return theName.str();
+		}
+
+		void AddVariantToMap(MeshVariantMap &variantMap, GLuint vao, const int components)
+		{
+			if(components == 0)
+			{
+				variantMap["unlit"] = vao;
+				variantMap["flat"] = vao;
+				return;
+			}
+
+			std::vector<std::string> currNames = GenerateNameList(components);
+
+			if(currNames.size() == 1)
+			{
+				variantMap[currNames[0]] = vao;
+				return;
+			}
+
+			std::sort(currNames.begin(), currNames.end());
+
+			std::ostringstream theName;
+			do
+			{
+				CalcName(theName, currNames);
+
+				variantMap[theName.str()] = vao;
+			}
+			while(std::next_permutation(currNames.begin(), currNames.end()));
+		}
+	}
+}

File glmesh/source/GenHelper.h

+
+#ifndef GLSDK_GENERATOR_HELPER_H
+#define GLSDK_GENERATOR_HELPER_H
+
+#include <string>
+#include "glmesh/Mesh.h"
+//Requires having included GL defines.
+
+namespace glmesh
+{
+
+	namespace gen
+	{
+		enum VariantComponents
+		{
+			VAR_COLOR		= 0x01,
+			VAR_NORMAL		= 0x02,
+			VAR_TEX_COORD	= 0x04,
+		};
+
+		//Generates a name for the given components. Just one of the names that can be used.
+		std::string GenerateNameForVariant(const int components);
+
+		void AddVariantToMap(MeshVariantMap &variantMap, GLuint vao, const int components);
+	}
+}
+
+#endif //GLSDK_GENERATOR_HELPER_H
+

File glmesh/source/Mesh.cpp

+
+#include <algorithm>
+#include <set>
+#include <glload/gl_all.hpp>
+#include <glload/gll.hpp>
+#include "glmesh/Mesh.h"
+
+
+namespace glmesh
+{
+	namespace
+	{
+		struct RenderCmd
+		{
+			bool isIndexedCmd;
+			GLenum ePrimType;
+			GLsizei vertexCount;
+
+			//Only valid if isIndexedCmd is false.
+			GLint startIndex;		
+
+			//Only valid if isIndexedCmd is true.
+			GLenum dataType;
+			GLintptr byteOffset;
+			GLint baseVertex;
+			bool hasPrimRestart;
+			int primRestart;
+		};
+	}
+
+	struct RenderCmdListData
+	{
+		RenderCmdListData() : hasRestartIndex(false) {}
+
+		bool hasRestartIndex;
+		GLint restartIndex;
+
+		std::vector<RenderCmd> cmds;
+	};
+
+	struct MeshData
+	{
+		MeshData(const std::vector<GLuint> &_bufferObjects, GLuint _mainVao,
+			const std::vector<RenderCmd> &_cmds, const MeshVariantMap &_variants)
+			: bufferObjects(_bufferObjects)
+			, mainVao(_mainVao)
+			, variants(_variants)
+			, cmds(_cmds)
+		{}
+
+		~MeshData()
+		{
+			std::set<GLuint> deletedVaos;
+
+			if(mainVao)
+			{
+				deletedVaos.insert(mainVao);
+				gl::DeleteVertexArrays(1, &mainVao);
+			}
+
+			for(MeshVariantMap::iterator currIt = variants.begin();
+				currIt != variants.end();
+				++currIt)
+			{
+				if(currIt->second && (deletedVaos.find(currIt->second) != deletedVaos.end()))
+				{
+					deletedVaos.insert(currIt->second);
+					gl::DeleteVertexArrays(1, &currIt->second);
+				}
+			}
+
+			gl::DeleteBuffers(bufferObjects.size(), &bufferObjects[0]);
+		}
+
+		std::vector<GLuint> bufferObjects;
+		GLuint mainVao;
+		MeshVariantMap variants;
+
+		std::vector<RenderCmd> cmds;
+	};
+
+	RenderCmdList::RenderCmdList()
+		: m_pData(new RenderCmdListData)
+	{}
+
+	RenderCmdList::RenderCmdList( class RenderCmdList &anOther )
+		: m_pData(new RenderCmdListData(*anOther.m_pData))
+	{}
+
+	RenderCmdList::~RenderCmdList()
+	{}
+
+	RenderCmdList& RenderCmdList::operator=( class RenderCmdList &anOther )
+	{
+		*m_pData = *anOther.m_pData;
+		return *this;
+	}
+
+	void RenderCmdList::DrawArrays( GLenum primitive, GLint startIndex, GLsizei vertexCount )
+	{
+		RenderCmd cmd;
+
+		cmd.isIndexedCmd = false;
+		cmd.ePrimType = primitive;
+		cmd.vertexCount = vertexCount;
+		cmd.startIndex = startIndex;
+
+		m_pData->cmds.push_back(cmd);
+	}
+
+	void RenderCmdList::DrawElements( GLenum primitive, GLsizei vertexCount, GLenum dataType,
+		GLintptr byteOffset, GLint baseVertex )
+	{
+		RenderCmd cmd;
+
+		cmd.isIndexedCmd = true;
+		cmd.ePrimType = primitive;
+		cmd.vertexCount = vertexCount;
+		cmd.dataType = dataType;
+		cmd.byteOffset = byteOffset;
+		cmd.baseVertex = baseVertex;
+		cmd.hasPrimRestart = m_pData->hasRestartIndex;
+		cmd.primRestart = m_pData->restartIndex;
+
+		m_pData->cmds.push_back(cmd);
+	}
+
+	void RenderCmdList::PrimitiveRestartIndex()
+	{
+		m_pData->hasRestartIndex = false;
+	}
+
+	void RenderCmdList::PrimitiveRestartIndex( GLuint index )
+	{
+		m_pData->hasRestartIndex = true;
+		m_pData->restartIndex = index;
+	}
+
+	Mesh::Mesh( const std::vector<GLuint> &bufferObjects, GLuint mainVao,
+		const RenderCmdList &renderCmds, const MeshVariantMap &variants )
+		: m_pData(new MeshData(bufferObjects, mainVao, renderCmds.m_pData->cmds, variants))
+	{}
+
+	Mesh::~Mesh()
+	{}
+
+	void Mesh::Render() const
+	{
+		if(m_pData->mainVao)
+		{
+			gl::BindVertexArray(m_pData->mainVao);
+			RenderWithCurrVao();
+			gl::BindVertexArray(0);
+		}
+	}
+
+	void Mesh::Render( const std::string &variantName ) const
+	{
+		MeshVariantMap::iterator currVariant = m_pData->variants.find(variantName);
+		if(currVariant == m_pData->variants.end() || (currVariant->second == 0))
+			return;
+
+		gl::BindVertexArray(currVariant->second);
+		RenderWithCurrVao();
+		gl::BindVertexArray(0);
+	}
+
+	void Mesh::RenderWithCurrVao() const
+	{
+		for(size_t loop = 0; loop < m_pData->cmds.size(); ++loop)
+		{
+			const RenderCmd &cmd = m_pData->cmds[loop];
+			if(cmd.isIndexedCmd)
+			{
+				if(cmd.hasPrimRestart)
+				{
+					gl::Enable(gl::GL_PRIMITIVE_RESTART);
+					gl::PrimitiveRestartIndex(cmd.primRestart);
+				}
+				else
+					gl::Disable(gl::GL_PRIMITIVE_RESTART);
+
+				if(cmd.baseVertex)
+					gl::DrawElementsBaseVertex(cmd.ePrimType, cmd.vertexCount, cmd.dataType,
+					reinterpret_cast<void*>(cmd.byteOffset), cmd.baseVertex);
+				else
+					gl::DrawElements(cmd.ePrimType, cmd.vertexCount, cmd.dataType,
+					reinterpret_cast<void*>(cmd.byteOffset));
+			}
+			else
+			{
+				gl::Disable(gl::GL_PRIMITIVE_RESTART);
+				gl::DrawArrays(cmd.ePrimType, cmd.startIndex, cmd.vertexCount);
+			}
+		}
+
+		gl::Disable(gl::GL_PRIMITIVE_RESTART);
+	}
+
+	bool Mesh::DoesMainExist() const
+	{
+		if(m_pData->mainVao)
+			return true;
+
+		return false;
+	}
+
+	bool Mesh::DoesVariantExist( const std::string &variantName ) const
+	{
+		MeshVariantMap::iterator currVariant = m_pData->variants.find(variantName);
+		if(currVariant == m_pData->variants.end() || (currVariant->second == 0))
+			return false;
+
+		return true;
+	}
+}

File glmesh/source/Quadrics.cpp

+
+#include <vector>
+#include <algorithm>
+#include <cmath>
+#include <glload/gl_all.hpp>
+#include <glload/gll.hpp>
+
+#include "glmesh/Mesh.h"
+#include "glmesh/Quadrics.h"
+#include "GenHelper.h"
+#include <glm/glm.hpp>
+
+namespace glmesh
+{
+	namespace gen
+	{
+		namespace
+		{
+			const float g_pi = 3.1415726f;
+			const float g_2pi = g_pi * 2.0f;
+		}
+
+		Mesh * UnitSphere( int numHorizSlices, int numVertSlices )
+		{
+			//The term "ring" refers to horizontal slices.
+			//The term "segment" refers to vertical slices.
+
+			//////////////////////////////////////////////////////////////////////////
+			// Generate the vertex attribute data.
+			numHorizSlices = std::max(numHorizSlices, 1);
+			numVertSlices = std::max(numVertSlices, 3);
+
+			//+2 to horizontal is for the top and bottom points, which are replicated due to texcoords.
+			size_t numRingVerts = numHorizSlices + 2;
+			//+1 to vertical is for doubling up on the initial point, again due to texcoords.
+			size_t numSegVerts = numVertSlices + 1;
+			size_t attribCount = numSegVerts * numRingVerts;
+
+			std::vector<glm::vec3> positions;
+			std::vector<glm::vec3> normals;
+			std::vector<glm::vec2> texCoords;
+
+			positions.reserve(attribCount);
+			normals.reserve(attribCount);
+			texCoords.reserve(attribCount);
+
+			float deltaSegTexCoord = 1.0f / numSegVerts;
+			float deltaRingTexCoord = 1.0f / numRingVerts;
+
+			for(int segment = 0; segment < numVertSlices; ++segment)
+			{
+				positions.push_back(glm::vec3(0.0f, 1.0f, 0.0f));
+				normals.push_back(glm::vec3(0.0f, 1.0f, 0.0f));
+				texCoords.push_back(glm::vec2(deltaSegTexCoord * segment, 1.0f));
+			}
+
+			positions.push_back(glm::vec3(0.0f, 1.0f, 0.0f));
+			normals.push_back(glm::vec3(0.0f, 1.0f, 0.0f));
+			texCoords.push_back(glm::vec2(1.0f, 0.0f));
+
+			float radThetaDelta = g_pi / (numHorizSlices + 1);
+			float radRhoDelta = g_2pi / numVertSlices;
+
+			for(int ring = 0; ring < numHorizSlices; ++ring)
+			{
+				float radTheta = radThetaDelta * (ring + 1);
+				float sinTheta = std::sin(radTheta);
+				float cosTheta = std::cos(radTheta);
+
+				float ringTexCoord = 1.0f - ((ring + 1) * deltaRingTexCoord);
+
+				for(int segment = 0; segment < numVertSlices; ++segment)
+				{
+					float radRho = radRhoDelta * segment;
+					float sinRho = std::sin(-radRho);
+					float cosRho = std::cos(-radRho);
+
+					glm::vec3 currPos(sinTheta * cosRho, cosTheta, sinTheta * sinRho);