Jason McKesson avatar Jason McKesson committed 800dbf2

Basic scene graph system is in place.

Comments (0)

Files changed (13)

Documents/Illumination/Tutorial 13.xml

                 we transform the camera-space position to clip-space as normal.</para>
             <para>The output <varname>mapping</varname> is a value that is used by the fragment
                 shader, as we will see below.</para>
-            <para>Since this vertex shader takes no inputs, our vertex array object does not need to
-                contain anything either. That is, we never call
+            <para><indexterm>
+                    <primary>Vertex Array Object</primary>
+                    <secondary>empty</secondary>
+                </indexterm>Since this vertex shader takes no inputs, our vertex array object does
+                not need to contain anything either. That is, we never call
                     <function>glEnableVertexAttribArray</function> on the VAO. Since no attribute
                 arrays are enabled, we also have no need for a buffer object to store vertex array
                 data. So we never call <function>glVertexAttribPointer</function>. We simply

Tut 13 Impostors/BasicImpostor.cpp

 }
 
 GLuint g_imposterVAO;
-GLuint g_imposterVBO;
 
 //Called after the window and OpenGL are initialized. Called exactly once, before the main loop.
 void init()
 
 	glBindBuffer(GL_UNIFORM_BUFFER, 0);
 
-	glGenBuffers(1, &g_imposterVBO);
-	glBindBuffer(GL_ARRAY_BUFFER, g_imposterVBO);
-	glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(float), NULL, GL_STATIC_DRAW);
-
+	//Empty Vertex Array Object.
 	glGenVertexArrays(1, &g_imposterVAO);
-//	glBindVertexArray(g_imposterVAO);
-//	glEnableVertexAttribArray(0);
-//	glVertexAttribPointer(0, 1, GL_FLOAT, GL_FALSE, 0, (void*)(0));
-
-	glBindVertexArray(0);
-	glBindBuffer(GL_ARRAY_BUFFER, 0);
 
 	CreateMaterials();
 }

Tut 17 Spotlight on Textures/Double Projection.cpp

 #include "../framework/Scene.h"
 #include <glm/glm.hpp>
 #include <glm/gtc/type_ptr.hpp>
+#include <glm/gtc/quaternion.hpp>
 #include <glm/gtc/matrix_transform.hpp>
 
 #define ARRAY_COUNT( array ) (sizeof( array ) / (sizeof( array[0] ) * (sizeof( array ) != sizeof(void*) || sizeof( array[0] ) <= sizeof(void*))))
 //View setup.
 glutil::ViewData g_initialView =
 {
-	glm::vec3(-60.257084f, 10.947238f, 62.636356f),
+	glm::vec3(0.0f, 0.0f, 0.0f),
 	glm::fquat(-0.972817f, -0.099283f, -0.211198f, -0.020028f),
 	30.0f,
 	0.0f
 }
 
 Framework::Scene *g_pScene = NULL;
+std::vector<Framework::NodeRef> g_nodes;
+Framework::Timer g_timer(Framework::Timer::TT_LOOP, 10.0f);
+
+class ColorUniformBinder : public Framework::StateBinder
+{
+public:
+	ColorUniformBinder()
+		: m_clrUnif(-1)
+		, m_clr(0.0f, 0.0f, 0.0f, 1.0f)	{}
+
+	void AssociateWithProgram(GLuint prog, const std::string &unifName)
+	{
+		m_clrUnif = glGetUniformLocation(prog, unifName.c_str());
+	}
+
+	void SetColor(const glm::vec4 &clr)	{ m_clr = clr; }
+
+	virtual void BindState() const
+	{
+		glUniform4fv(m_clrUnif, 1, glm::value_ptr(m_clr));
+	}
+
+	virtual void UnbindState() const {}
+
+private:
+	GLint m_clrUnif;
+	glm::vec4 m_clr;
+};
+
+ColorUniformBinder g_clrUnif;
+
+void LoadAndSetupScene()
+{
+	g_nodes.clear();
+	g_pScene = new Framework::Scene("sceneTest.xml");
+	g_nodes.push_back(g_pScene->FindNode("blue"));
+	g_nodes.push_back(g_pScene->FindNode("user"));
+
+	GLuint colorProg = g_pScene->FindProgram("p_colored");
+
+	//No more things that can throw.
+	g_clrUnif.AssociateWithProgram(colorProg, "objectColor");
+	g_nodes[1].SetStateBinder(&g_clrUnif);
+	g_clrUnif.SetColor(glm::vec4(0.1f, 1.0f, 0.1f, 1.0f));
+}
 
 //Called after the window and OpenGL are initialized. Called exactly once, before the main loop.
 void init()
 {
-	try
-	{
-		g_pScene = new Framework::Scene("test.scene");
-//		InitializePrograms();
-	}
-	catch(std::exception &except)
-	{
-		printf("%s\n", except.what());
-		throw;
-	}
-
 	glutMouseFunc(MouseButton);
 	glutMotionFunc(MouseMotion);
 	glutMouseWheelFunc(MouseWheel);
 	glDepthFunc(GL_LEQUAL);
 	glDepthRange(depthZNear, depthZFar);
 	glEnable(GL_DEPTH_CLAMP);
+	glEnable(GL_FRAMEBUFFER_SRGB);
 
 	//Setup our Uniform Buffers
 	glGenBuffers(1, &g_projectionUniformBuffer);
 	glBindBufferRange(GL_UNIFORM_BUFFER, g_projectionBlockIndex, g_projectionUniformBuffer,
 		0, sizeof(ProjectionBlock));
 
+	try
+	{
+		LoadAndSetupScene();
+//		InitializePrograms();
+	}
+	catch(std::exception &except)
+	{
+		printf("%s\n", except.what());
+		throw;
+	}
+
 /*
 	glGenBuffers(1, &g_lightUniformBuffer);
 	glBindBuffer(GL_UNIFORM_BUFFER, g_lightUniformBuffer);
 	if(!g_pScene)
 		return;
 
+	g_timer.Update();
+
 	glClearColor(0.8f, 0.8f, 0.8f, 1.0f);
 	glClearDepth(1.0f);
 	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+	g_nodes[0].NodeSetOrient(glm::rotate(glm::fquat(),
+		360.0f * g_timer.GetAlpha(), glm::vec3(0.0f, 1.0f, 0.0f)));
+
+	g_pScene->Render(g_viewPole.CalcMatrix());
 /*
     if(!g_pLightEnv)
         return;
 		}
 	}
 
-	glutPostRedisplay();
-*/
+	*/
+
+    glutPostRedisplay();
 	glutSwapBuffers();
 }
 
 		glutLeaveMainLoop();
 		return;
 	case 32:
+		g_nodes[0].NodeSetTrans(glm::vec3(0.0f, 0.0f, 0.0f));
+		break;
+	case 'i':
+		g_nodes[0].NodeOffset(glm::vec3(0.0f, 1.0f, 0.0f));
+		break;
+	case 'j':
+		g_nodes[0].NodeOffset(glm::vec3(0.0f, -1.0f, 0.0f));
+		break;
+	case '\r':
+		{
+			std::auto_ptr<Framework::Scene> pOldScene(g_pScene);
+			g_pScene = NULL;
+			std::vector<Framework::NodeRef> tmpNodes;
+			tmpNodes.swap(g_nodes);
+			try
+			{
+				LoadAndSetupScene();
+			}
+			catch(std::exception &except)
+			{
+				printf("Failed to reload, due to: %s\n", except.what());
+				if(g_pScene)
+					delete g_pScene;
+				g_pScene = pOldScene.release();
+				g_nodes.swap(tmpNodes);
+				return;
+			}
+		}
 		break;
 	}
 

framework/Mesh.cpp

 			}
 
 			strStream.flush();
-			const std::string &strTest = strStream.str();
 
 			//Parse the text stream.
 			pAttribType->ParseFunc(dataArray, strStream);

framework/Scene.cpp

 #include <string>
+#include <strstream>
 #include <vector>
+#include <set>
+#include <map>
 #include <exception>
+#include <algorithm>
 #include <memory>
 
 #include <istream>
 #include "framework.h"
 #include "Scene.h"
 #include "Mesh.h"
+#include <glutil/Shader.h>
 
+#include "rapidxml.hpp"
+#include "rapidxml_helpers.h"
+#include <glm/glm.hpp>
+#include <glm/gtc/quaternion.hpp>
+#include <glm/gtc/type_ptr.hpp>
+#include <glm/gtc/matrix_transform.hpp>
+
+
+#define PARSE_THROW(cond, message)\
+	if(!(cond))\
+		throw std::runtime_error(message);
 
 
 namespace Framework
 {
+	using rapidxml::xml_document;
+	using rapidxml::xml_node;
+	using rapidxml::xml_attribute;
+	using rapidxml::make_string;
+
+
+	namespace
+	{
+		void ThrowAttrib(const xml_attribute<> &attrib, const std::string &msg)
+		{
+			std::string name = make_string(attrib);
+			throw std::runtime_error("Attribute " + name + " " + msg);
+		}
+
+		template<typename PairType>
+		void DeleteSecond(PairType &value)
+		{
+			delete value.second;
+			value.second = NULL;
+		}
+
+		template<typename DeleteType>
+		void DeleteThis(DeleteType &value) { delete value; }
+
+		void BindBinder(const StateBinder *pState) {pState->BindState();}
+		void UnbindBinder(const StateBinder *pState) {pState->UnbindState();}
+	}
+
+	class SceneMesh
+	{
+	public:
+		SceneMesh(const std::string &filename)
+			: m_pMesh(new Framework::Mesh(filename))
+		{}
+
+		~SceneMesh()
+		{
+			delete m_pMesh;
+		}
+
+		void Render() const
+		{
+			m_pMesh->Render();
+		}
+
+	private:
+		Framework::Mesh *m_pMesh;
+	};
+
+	class SceneProgram
+	{
+	public:
+		SceneProgram(GLuint programObj, GLint matrixLoc, GLint normalMatLoc)
+			: m_programObj(programObj)
+			, m_matrixLoc(matrixLoc)
+			, m_normalMatLoc(normalMatLoc)
+		{}
+
+		~SceneProgram()
+		{
+			glDeleteProgram(m_programObj);
+		}
+
+		GLint GetMatrixLoc() const {return m_matrixLoc;}
+		GLint GetNormalMatLoc() const {return m_normalMatLoc;}
+
+		void UseProgram() const {glUseProgram(m_programObj);}
+
+		GLuint GetProgram() const {return m_programObj;}
+
+	private:
+		GLuint m_programObj;
+		GLint m_matrixLoc;
+		GLint m_normalMatLoc;
+	};
+
+	struct Transform
+	{
+		Transform()
+			: m_orient(1.0f, 0.0f, 0.0f, 0.0f)
+			, m_scale(1.0f, 1.0f, 1.0f)
+			, m_trans(0.0f, 0.0f, 0.0f)
+		{}
+
+		glm::mat4 GetMatrix() const
+		{
+			glm::mat4 ret;
+			ret = glm::translate(ret, m_trans);
+			ret *= glm::mat4_cast(m_orient);
+			ret = glm::scale(ret, m_scale);
+			return ret;
+		}
+
+		glm::fquat m_orient;
+		glm::vec3 m_scale;
+		glm::vec3 m_trans;
+	};
+
+	class SceneNode
+	{
+	public:
+		SceneNode(SceneMesh *pMesh, SceneProgram *pProg, const glm::vec3 &nodePos)
+			: m_pMesh(pMesh)
+			, m_pProg(pProg)
+		{
+			m_nodeTm.m_trans = nodePos;
+		}
+
+		void NodeSetScale( const glm::vec3 &scale )
+		{
+			m_nodeTm.m_scale = scale;
+		}
+
+		void NodeRotate( const glm::fquat &orient )
+		{
+			m_nodeTm.m_orient = m_nodeTm.m_orient * orient;
+		}
+
+		void NodeSetOrient( const glm::fquat &orient )
+		{
+			m_nodeTm.m_orient = orient;
+		}
+
+		void SetNodeOrient(const glm::fquat &nodeOrient)
+		{
+			m_nodeTm.m_orient = glm::normalize(nodeOrient);
+		}
+
+		void SetNodeScale(const glm::vec3 &nodeScale)
+		{
+			m_nodeTm.m_scale = nodeScale;
+		}
+
+		void Render(glm::mat4 baseMat) const
+		{
+			baseMat *= m_nodeTm.GetMatrix();
+			glm::mat4 objMat = baseMat * m_objTm.GetMatrix();
+
+			m_pProg->UseProgram();
+			glUniformMatrix4fv(m_pProg->GetMatrixLoc(), 1, GL_FALSE, glm::value_ptr(objMat));
+
+			if(m_pProg->GetNormalMatLoc() != -1)
+			{
+				glm::mat4 normMat = glm::transpose(glm::inverse(objMat));
+				glUniformMatrix4fv(m_pProg->GetNormalMatLoc(), 1, GL_FALSE,
+					glm::value_ptr(normMat));
+			}
+
+			std::for_each(m_binders.begin(), m_binders.end(), BindBinder);
+			m_pMesh->Render();
+			std::for_each(m_binders.rbegin(), m_binders.rend(), UnbindBinder);
+			glUseProgram(0);
+		}
+
+		void NodeOffset(const glm::vec3 &offset)
+		{
+			m_nodeTm.m_trans += offset;
+		}
+
+		void NodeSetTrans(const glm::vec3 &offset)
+		{
+			m_nodeTm.m_trans = offset;
+		}
+
+		void SetStateBinder(StateBinder *pBinder)
+		{
+			m_binders.push_back(pBinder);
+		}
+
+	private:
+		SceneMesh *m_pMesh;		//Unmanaged. We are deleted first, so these should always be real values.
+		SceneProgram *m_pProg;	//Unmanaged. We are deleted first, so these should always be real values.
+
+		std::vector<StateBinder*> m_binders;
+
+		Transform m_nodeTm;
+		Transform m_objTm;
+	};
+
+	typedef std::map<std::string, SceneMesh*> MeshMap;
+	typedef std::map<std::string, SceneProgram*> ProgramMap;
+	typedef std::map<std::string, SceneNode*> NodeMap;
+
 	class SceneImpl
 	{
+	private:
+		MeshMap m_meshes;
+		ProgramMap m_progs;
+		NodeMap m_nodes;
+
+		std::vector<SceneNode *> m_rootNodes;
+
 	public:
 		SceneImpl(const std::string &filename)
 		{
 			if(!fileStream.is_open())
 				throw std::runtime_error("Could not open the scene file.");
 
-			while(!fileStream.fail() && !fileStream.eof())
-				LoadElement(fileStream);
+			std::vector<char> fileData;
+			fileData.reserve(2000);
+			fileData.insert(fileData.end(), std::istreambuf_iterator<char>(fileStream),
+				std::istreambuf_iterator<char>());
+			fileData.push_back('\0');
 
-			std::cout << "EOF" << std::endl;
+			xml_document<> doc;
+
+			try
+			{
+				doc.parse<0>(&fileData[0]);
+			}
+			catch(rapidxml::parse_error &e)
+			{
+				std::cout << filename << ": Parse error in scene file." << std::endl;
+				std::cout << e.what() << std::endl << e.where<char>() << std::endl;
+				throw;
+			}
+
+			xml_node<> *pSceneNode = doc.first_node("scene");
+			PARSE_THROW(pSceneNode, "Scene node not found in scene file.");
+
+			try
+			{
+				ReadMeshes(*pSceneNode);
+				ReadPrograms(*pSceneNode);
+				ReadNodes(NULL, *pSceneNode);
+			}
+			catch(...)
+			{
+				std::for_each(m_meshes.begin(), m_meshes.end(), DeleteSecond<typename MeshMap::value_type>);
+				std::for_each(m_progs.begin(), m_progs.end(), DeleteSecond<typename ProgramMap::value_type>);
+				std::for_each(m_nodes.begin(), m_nodes.end(), DeleteSecond<typename NodeMap::value_type>);
+				throw;
+			}
 		}
 
-	private:
-		void LoadElement(std::istream &fileStream)
+		~SceneImpl()
 		{
+			std::for_each(m_nodes.begin(), m_nodes.end(), DeleteSecond<typename NodeMap::value_type>);
+			std::for_each(m_progs.begin(), m_progs.end(), DeleteSecond<typename ProgramMap::value_type>);
+			std::for_each(m_meshes.begin(), m_meshes.end(), DeleteSecond<typename MeshMap::value_type>);
 		}
 
-		void ReadProgram(std::istream &fileStream)
+		void Render(const glm::mat4 &cameraMatrix) const
 		{
+			for(NodeMap::const_iterator &theIt = m_nodes.begin();
+				theIt != m_nodes.end();
+				++theIt)
+			{
+				theIt->second->Render(cameraMatrix);
+			}
 		}
 
-		void ReadMesh(std::istream &fileStream)
+		NodeRef FindNode(const std::string &nodeName)
 		{
+			NodeMap::iterator theIt = m_nodes.find(nodeName);
+			if(theIt == m_nodes.end())
+				throw std::runtime_error("Could not find the node named: " + nodeName);
+
+			return NodeRef(theIt->second);
 		}
 
-		std::string ReadFilename(std::istream &fileStream)
+		GLuint FindProgram(const std::string &progName)
 		{
+			ProgramMap::iterator theIt = m_progs.find(progName);
+			if(theIt == m_progs.end())
+				throw std::runtime_error("Could not find the program named: " + progName);
+
+			return theIt->second->GetProgram();
+		}
+
+
+	private:
+
+		void ReadMeshes(const xml_node<> &scene)
+		{
+			for(const xml_node<> *pMeshNode = scene.first_node("mesh");
+				pMeshNode;
+				pMeshNode = pMeshNode->next_sibling("mesh"))
+			{
+				ReadMesh(*pMeshNode);
+			}
+		}
+
+		void ReadMesh(const xml_node<> &meshNode)
+		{
+			const xml_attribute<> *pNameNode = meshNode.first_attribute("xml:id");
+			const xml_attribute<> *pFilenameNode = meshNode.first_attribute("file");
+
+			PARSE_THROW(pNameNode, "Mesh found with no `xml:id` name specified.");
+			PARSE_THROW(pFilenameNode, "Mesh found with no `file` filename specified.");
+
+			std::string name = make_string(*pNameNode);
+			if(m_meshes.find(name) != m_meshes.end())
+				throw std::runtime_error("The mesh named \"" + name + "\" already exists.");
+
+			m_meshes[name] = NULL;
+
+			SceneMesh *pMesh = new SceneMesh(make_string(*pFilenameNode));
+
+			m_meshes[name] = pMesh;
+
+			std::cout << "Mesh: \"" << pNameNode->value() << "\", \"" << pFilenameNode->value()
+				<< "\"" << std::endl;
+		}
+
+		void ReadPrograms(const xml_node<> &scene)
+		{
+			for(const xml_node<> *pProgNode = scene.first_node("prog");
+				pProgNode;
+				pProgNode = pProgNode->next_sibling("prog"))
+			{
+				ReadProgram(*pProgNode);
+			}
+		}
+
+		void ReadProgram(const xml_node<> &progNode)
+		{
+			const xml_attribute<> *pNameNode = progNode.first_attribute("xml:id");
+			const xml_attribute<> *pVertexShaderNode = progNode.first_attribute("vert");
+			const xml_attribute<> *pFragmentShaderNode = progNode.first_attribute("frag");
+			const xml_attribute<> *pModelMatrixNode = progNode.first_attribute("model-to-camera");
+
+			PARSE_THROW(pNameNode, "Program found with no `xml:id` name specified.");
+			PARSE_THROW(pVertexShaderNode, "Program found with no `vert` vertex shader specified.");
+			PARSE_THROW(pFragmentShaderNode, "Program found with no `frag` fragment shader specified.");
+			PARSE_THROW(pModelMatrixNode, "Program found with no model-to-camera matrix uniform name specified.");
+
+			//Optional.
+			const xml_attribute<> *pNormalMatrixNode = progNode.first_attribute("normal-model-to-camera");
+			const xml_attribute<> *pGeometryShaderNode = progNode.first_attribute("geom");
+
+			std::string name = make_string(*pNameNode);
+			if(m_progs.find(name) != m_progs.end())
+				throw std::runtime_error("The program named \"" + name + "\" already exists.");
+
+			m_progs[name] = NULL;
+
+			std::vector<GLuint> shaders;
+			GLuint program = 0;
+
+			try
+			{
+				shaders.push_back(LoadShader(GL_VERTEX_SHADER, make_string(*pVertexShaderNode)));
+				shaders.push_back(LoadShader(GL_FRAGMENT_SHADER, make_string(*pFragmentShaderNode)));
+				if(pGeometryShaderNode)
+					shaders.push_back(LoadShader(GL_GEOMETRY_SHADER, make_string(*pGeometryShaderNode)));
+				program = glutil::LinkProgram(shaders);
+			}
+			catch(std::exception &)
+			{
+				std::for_each(shaders.begin(), shaders.end(), glDeleteShader);
+				throw;
+			}
+
+			std::for_each(shaders.begin(), shaders.end(), glDeleteShader);
+
+			std::string matrixName = make_string(*pModelMatrixNode);
+			GLint matrixLoc = glGetUniformLocation(program, matrixName.c_str());
+			if(matrixLoc == -1)
+			{
+				glDeleteProgram(program);
+				throw std::runtime_error("Could not find the matrix uniform " + matrixName +
+					" in program " + name);
+			}
+
+			GLint normalMatLoc = -1;
+			if(pNormalMatrixNode)
+			{
+				matrixName = make_string(*pNormalMatrixNode);
+				normalMatLoc = glGetUniformLocation(program, matrixName.c_str());
+				if(normalMatLoc == -1)
+				{
+					glDeleteProgram(program);
+					throw std::runtime_error("Could not find the normal matrix uniform " + matrixName +
+						" in program " + name);
+				}
+			}
+
+			m_progs[name] = new SceneProgram(program, matrixLoc, normalMatLoc);
+
+			std::cout << "Program: \"" << pNameNode->value() << "\"" << std::endl;
+			std::cout << "\tVertex Shader: \"" << pVertexShaderNode->value() << "\"" << std::endl;
+			if(pGeometryShaderNode)
+				std::cout << "\tGeometry Shader: \"" << pGeometryShaderNode->value() << "\"" << std::endl;
+			std::cout << "\tFragment Shader: \"" << pFragmentShaderNode->value() << "\"" << std::endl;
+			std::cout << "\tModel Matrix uniform: \"" << pModelMatrixNode->value() << "\"" << std::endl;
+			if(pNormalMatrixNode)
+				std::cout << "\tNormal Matrix uniform: \"" << pNormalMatrixNode->value() << "\"" << std::endl;
+
+			ReadProgramContents(program, progNode);
+		}
+
+		void ReadProgramContents(GLuint program, const xml_node<> &progNode)
+		{
+			std::set<std::string> blockBindings;
+			std::set<std::string> samplerBindings;
+
+			for(const xml_node<> *pChildNode = progNode.first_node();
+				pChildNode;
+				pChildNode = pChildNode->next_sibling())
+			{
+				if(pChildNode->type() != rapidxml::node_element)
+					continue;
+
+				const std::string childName = std::string(pChildNode->name(), pChildNode->name_size());
+				if(childName == "block")
+				{
+					const xml_attribute<> *pNameNode = pChildNode->first_attribute("name");
+					const xml_attribute<> *pBindingNode = pChildNode->first_attribute("binding");
+
+					PARSE_THROW(pNameNode, "Program `block` element with no `name`.");
+					PARSE_THROW(pBindingNode, "Program `block` element with no `binding`.");
+
+					std::string name = make_string(*pNameNode);
+					if(blockBindings.find(name) != blockBindings.end())
+						throw std::runtime_error("The uniform block " + name + " is used twice in the same program.");
+
+					blockBindings.insert(name);
+
+					GLuint blockIx = glGetUniformBlockIndex(program, name.c_str());
+					if(blockIx == GL_INVALID_INDEX)
+						throw std::runtime_error("The uniform block " + name + " could not be found.");
+
+					int bindPoint = rapidxml::attrib_to_int(*pBindingNode, ThrowAttrib);
+					glUniformBlockBinding(program, blockIx, bindPoint);
+
+					std::cout << "\t->Block: \"" << pNameNode->value() << "\", "
+						<< bindPoint << std::endl;
+				}
+				else if(childName == "sampler")
+				{
+					const xml_attribute<> *pNameNode = pChildNode->first_attribute("name");
+					const xml_attribute<> *pTexunitNode = pChildNode->first_attribute("unit");
+
+					PARSE_THROW(pNameNode, "Program `sampler` element with no `name`.");
+					PARSE_THROW(pTexunitNode, "Program `sampler` element with no `unit`.");
+
+					std::string name = make_string(*pNameNode);
+					if(samplerBindings.find(name) != samplerBindings.end())
+						throw std::runtime_error("A sampler " + name + " is used twice within the same program.");
+
+					samplerBindings.insert(name);
+
+					GLint samplerLoc = glGetUniformLocation(program, name.c_str());
+					if(samplerLoc == -1)
+						throw std::runtime_error("The sampler " + name + " could not be found.");
+
+					GLint textureUnit = rapidxml::attrib_to_int(*pTexunitNode, ThrowAttrib);
+					glUseProgram(program);
+					glUniform1i(samplerLoc, textureUnit);
+					glUseProgram(0);
+
+					std::cout << "\t->Sampler: \"" << pNameNode->value() << "\", "
+						<<textureUnit << std::endl;
+				}
+				else
+				{
+					//Bad node. Die.
+					throw std::runtime_error("Unknown element found in program.");
+				}
+			}
+		}
+
+		void ReadNodes(SceneNode *pParent, const xml_node<> &scene)
+		{
+			for(const xml_node<> *pNodeNode = scene.first_node("node");
+				pNodeNode;
+				pNodeNode = pNodeNode->next_sibling("node"))
+			{
+				ReadNode(pParent, *pNodeNode);
+			}
+		}
+
+		void ReadNode(SceneNode *pParent, const xml_node<> &nodeNode)
+		{
+			const xml_attribute<> *pNameNode = nodeNode.first_attribute("name");
+			const xml_attribute<> *pMeshNode = nodeNode.first_attribute("mesh");
+			const xml_attribute<> *pProgNode = nodeNode.first_attribute("prog");
+
+			PARSE_THROW(pNameNode, "Node found with no `name` name specified.");
+			PARSE_THROW(pMeshNode, "Node found with no `mesh` name specified.");
+			PARSE_THROW(pProgNode, "Node found with no `prog` name specified.");
+
+			const xml_attribute<> *pPositionNode = nodeNode.first_attribute("pos");
+			const xml_attribute<> *pOrientNode = nodeNode.first_attribute("orient");
+			const xml_attribute<> *pScaleNode = nodeNode.first_attribute("scale");
+
+			PARSE_THROW(pPositionNode, "Node found with no `pos` specified.");
+
+			std::string name = make_string(*pNameNode);
+			if(m_nodes.find(name) != m_nodes.end())
+				throw std::runtime_error("The node named \"" + name + "\" already exists.");
+
+			m_nodes[name] = NULL;
+
+			std::string meshName = make_string(*pMeshNode);
+			MeshMap::iterator meshIt = m_meshes.find(meshName);
+			if(meshIt == m_meshes.end())
+			{
+				throw std::runtime_error("The node named \"" + name + 
+					"\" references the mesh \"" + meshName + "\" which does not exist.");
+			}
+
+			std::string progName = make_string(*pProgNode);
+			ProgramMap::iterator progIt = m_progs.find(progName);
+			if(progIt == m_progs.end())
+			{
+				throw std::runtime_error("The node named \"" + name + 
+					"\" references the program \"" + progName + "\" which does not exist.");
+			}
+
+			glm::vec3 nodePos = rapidxml::attrib_to_vec3(*pPositionNode, ThrowAttrib);
+
+			SceneNode *pNode = new SceneNode(meshIt->second, progIt->second, nodePos);
+			m_nodes[name] = pNode;
+
+			//TODO: parent/child nodes.
+			if(!pParent)
+				m_rootNodes.push_back(pNode);
+
+			if(pOrientNode)
+				pNode->SetNodeOrient(rapidxml::attrib_to_quat(*pOrientNode, ThrowAttrib));
+
+			if(pScaleNode)
+			{
+				if(rapidxml::attrib_is_vec3(*pScaleNode))
+					pNode->SetNodeScale(rapidxml::attrib_to_vec3(*pScaleNode, ThrowAttrib));
+				else
+				{
+					float unifScale = rapidxml::attrib_to_float(*pScaleNode, ThrowAttrib);
+					pNode->SetNodeScale(glm::vec3(unifScale));
+				}
+			}
+
+			std::cout << "Node: \"" << pNameNode->value() << "\"";
+			std::cout << "\tMesh: \"" << pMeshNode->value() << "\"";
+			std::cout << "\tProgram: \"" << pProgNode->value() << "\"" << std::endl;
+
+			std::cout << "\tPosition:    " << pPositionNode->value() << "\"" << std::endl;
+			if(pOrientNode)
+				std::cout << "\tOrientation: " << pOrientNode->value() << "\"" << std::endl;
+			if(pScaleNode)
+				std::cout << "\tScale:       " << pScaleNode->value() << "\"" << std::endl;
+
+			ReadNodeNotes(nodeNode);
+		}
+
+		void ReadNodeNotes(const xml_node<> &nodeNode)
+		{
+			for(const xml_node<> *pNoteNode = nodeNode.first_node("note");
+				pNoteNode;
+				pNoteNode = pNoteNode->next_sibling("note"))
+			{
+				const xml_node<> &noteNode = *pNoteNode;
+				const xml_attribute<> *pNameNode = noteNode.first_attribute("name");
+				PARSE_THROW(pNameNode, "Notations on nodes must have a `name` attribute.");
+
+				std::cout << "\t->Note: \"" << pNameNode->value() << "\"" << std::endl;
+				std::cout << "\t\t->" << noteNode.value() << "<-" << std::endl;
+			}
 		}
 	};
 
+	void NodeRef::NodeSetScale( const glm::vec3 &scale )
+	{
+		m_pNode->NodeSetScale(scale);
+	}
+
+	void NodeRef::NodeSetScale( float scale )
+	{
+		m_pNode->NodeSetScale(glm::vec3(scale));
+	}
+
+	void NodeRef::NodeRotate( const glm::fquat &orient )
+	{
+		m_pNode->NodeRotate(orient);
+	}
+
+	void NodeRef::NodeSetOrient( const glm::fquat &orient )
+	{
+		m_pNode->NodeSetOrient(orient);
+	}
+
+	void NodeRef::NodeOffset( const glm::vec3 &offset )
+	{
+		m_pNode->NodeOffset(offset);
+	}
+
+	void NodeRef::NodeSetTrans( const glm::vec3 &offset )
+	{
+		m_pNode->NodeSetTrans(offset);
+	}
+
+	void NodeRef::SetStateBinder( StateBinder *pBinder )
+	{
+		m_pNode->SetStateBinder(pBinder);
+	}
+
 	Scene::Scene( const std::string &filename )
 		: m_pImpl(new SceneImpl(filename))
 	{}
 		delete m_pImpl;
 	}
 
-	void Scene::Render() const
+	void Scene::Render( const glm::mat4 &cameraMatrix ) const
 	{
+		m_pImpl->Render(cameraMatrix);
+	}
 
+	Framework::NodeRef Scene::FindNode( const std::string &nodeName )
+	{
+		return m_pImpl->FindNode(nodeName);
+	}
+
+	GLuint Scene::FindProgram( const std::string &progName )
+	{
+		return m_pImpl->FindProgram(progName);
 	}
 }

framework/Scene.h

 #define FRAMEWORK_SCENE_H
 
 #include <string>
+#include <map>
+#include <glm/glm.hpp>
+#include <glm/gtc/quaternion.hpp>
 
 namespace Framework
 {
 	class SceneImpl;
+	class SceneNode;
+
+	class StateBinder
+	{
+	public:
+		virtual ~StateBinder() {}
+
+		//The current program will be in use when this is called.
+		virtual void BindState() const = 0;
+
+		//The current program will be in use when this is called.
+		virtual void UnbindState() const = 0;
+	};
+
+	class NodeRef
+	{
+	public:
+		void NodeSetScale(const glm::vec3 &scale);
+		void NodeSetScale(float scale);
+
+		//Right-multiplies the given orientation to the current one.
+		void NodeRotate(const glm::fquat &orient);
+		//Sets the current orientation to the given one.
+		void NodeSetOrient(const glm::fquat &orient);
+
+		//Adds the offset to the current translation.
+		void NodeOffset(const glm::vec3 &offset);
+		//Sets the current translation to the given one.
+		void NodeSetTrans(const glm::vec3 &offset);
+
+		//This object does *NOT* claim ownership of the pointer.
+		//You must ensure that it stays around so long as this Scene exists.
+		void SetStateBinder(StateBinder *pBinder);
+
+	private:
+		NodeRef();	//No default-construction.
+		explicit NodeRef(SceneNode *pNode) : m_pNode(pNode) {}
+		SceneNode *m_pNode;
+
+		friend class SceneImpl;
+	};
 
 	class Scene
 	{
 		Scene(const std::string &filename);
 		~Scene();
 
-		void Render() const;
+		void Render(const glm::mat4 &cameraMatrix) const;
+
+		NodeRef FindNode(const std::string &nodeName);
+
+		GLuint FindProgram(const std::string &progName);
 
 	private:
 		SceneImpl *m_pImpl;
 	};
+
 }
 
 #endif //FRAMEWORK_SCENE_H

framework/framework.lua

 		
 		files  "../framework/*.cpp"
 		files  "../framework/*.h"
+		files  "../framework/*.hpp"
 		excludes "../framework/empty.cpp"
 		
 		targetdir "../framework/lib"

framework/rapidxml-license.txt

+Use of this software is granted under one of the following two licenses,
+to be chosen freely by the user.
+
+1. Boost Software License - Version 1.0 - August 17th, 2003
+===============================================================================
+
+Copyright (c) 2006, 2007 Marcin Kalicinski
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+2. The MIT License
+===============================================================================
+
+Copyright (c) 2006, 2007 Marcin Kalicinski
+
+Permission is hereby granted, free of charge, to any person obtaining a copy 
+of this software and associated documentation files (the "Software"), to deal 
+in the Software without restriction, including without limitation the rights 
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+of the Software, and to permit persons to whom the Software is furnished to do so, 
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all 
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 
+IN THE SOFTWARE.

framework/rapidxml.hpp

+#ifndef RAPIDXML_HPP_INCLUDED
+#define RAPIDXML_HPP_INCLUDED
+
+// Copyright (C) 2006, 2009 Marcin Kalicinski
+// Version 1.13
+// Revision $DateTime: 2009/05/13 01:46:17 $
+//! \file rapidxml.hpp This file contains rapidxml parser and DOM implementation
+
+// If standard library is disabled, user must provide implementations of required functions and typedefs
+#if !defined(RAPIDXML_NO_STDLIB)
+    #include <cstdlib>      // For std::size_t
+    #include <cassert>      // For assert
+    #include <new>          // For placement new
+#endif
+
+// On MSVC, disable "conditional expression is constant" warning (level 4). 
+// This warning is almost impossible to avoid with certain types of templated code
+#ifdef _MSC_VER
+    #pragma warning(push)
+    #pragma warning(disable:4127)   // Conditional expression is constant
+#endif
+
+///////////////////////////////////////////////////////////////////////////
+// RAPIDXML_PARSE_ERROR
+    
+#if defined(RAPIDXML_NO_EXCEPTIONS)
+
+#define RAPIDXML_PARSE_ERROR(what, where) { parse_error_handler(what, where); assert(0); }
+
+namespace rapidxml
+{
+    //! When exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, 
+    //! this function is called to notify user about the error.
+    //! It must be defined by the user.
+    //! <br><br>
+    //! This function cannot return. If it does, the results are undefined.
+    //! <br><br>
+    //! A very simple definition might look like that:
+    //! <pre>
+    //! void %rapidxml::%parse_error_handler(const char *what, void *where)
+    //! {
+    //!     std::cout << "Parse error: " << what << "\n";
+    //!     std::abort();
+    //! }
+    //! </pre>
+    //! \param what Human readable description of the error.
+    //! \param where Pointer to character data where error was detected.
+    void parse_error_handler(const char *what, void *where);
+}
+
+#else
+    
+#include <exception>    // For std::exception
+
+#define RAPIDXML_PARSE_ERROR(what, where) throw parse_error(what, where)
+
+namespace rapidxml
+{
+
+    //! Parse error exception. 
+    //! This exception is thrown by the parser when an error occurs. 
+    //! Use what() function to get human-readable error message. 
+    //! Use where() function to get a pointer to position within source text where error was detected.
+    //! <br><br>
+    //! If throwing exceptions by the parser is undesirable, 
+    //! it can be disabled by defining RAPIDXML_NO_EXCEPTIONS macro before rapidxml.hpp is included.
+    //! This will cause the parser to call rapidxml::parse_error_handler() function instead of throwing an exception.
+    //! This function must be defined by the user.
+    //! <br><br>
+    //! This class derives from <code>std::exception</code> class.
+    class parse_error: public std::exception
+    {
+    
+    public:
+    
+        //! Constructs parse error
+        parse_error(const char *what, void *where)
+            : m_what(what)
+            , m_where(where)
+        {
+        }
+
+        //! Gets human readable description of error.
+        //! \return Pointer to null terminated description of the error.
+        virtual const char *what() const throw()
+        {
+            return m_what;
+        }
+
+        //! Gets pointer to character data where error happened.
+        //! Ch should be the same as char type of xml_document that produced the error.
+        //! \return Pointer to location within the parsed string where error occured.
+        template<class Ch>
+        Ch *where() const
+        {
+            return reinterpret_cast<Ch *>(m_where);
+        }
+
+    private:  
+
+        const char *m_what;
+        void *m_where;
+
+    };
+}
+
+#endif
+
+///////////////////////////////////////////////////////////////////////////
+// Pool sizes
+
+#ifndef RAPIDXML_STATIC_POOL_SIZE
+    // Size of static memory block of memory_pool.
+    // Define RAPIDXML_STATIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value.
+    // No dynamic memory allocations are performed by memory_pool until static memory is exhausted.
+    #define RAPIDXML_STATIC_POOL_SIZE (64 * 1024)
+#endif
+
+#ifndef RAPIDXML_DYNAMIC_POOL_SIZE
+    // Size of dynamic memory block of memory_pool.
+    // Define RAPIDXML_DYNAMIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value.
+    // After the static block is exhausted, dynamic blocks with approximately this size are allocated by memory_pool.
+    #define RAPIDXML_DYNAMIC_POOL_SIZE (64 * 1024)
+#endif
+
+#ifndef RAPIDXML_ALIGNMENT
+    // Memory allocation alignment.
+    // Define RAPIDXML_ALIGNMENT before including rapidxml.hpp if you want to override the default value, which is the size of pointer.
+    // All memory allocations for nodes, attributes and strings will be aligned to this value.
+    // This must be a power of 2 and at least 1, otherwise memory_pool will not work.
+    #define RAPIDXML_ALIGNMENT sizeof(void *)
+#endif
+
+namespace rapidxml
+{
+    // Forward declarations
+    template<class Ch> class xml_node;
+    template<class Ch> class xml_attribute;
+    template<class Ch> class xml_document;
+    
+    //! Enumeration listing all node types produced by the parser.
+    //! Use xml_node::type() function to query node type.
+    enum node_type
+    {
+        node_document,      //!< A document node. Name and value are empty.
+        node_element,       //!< An element node. Name contains element name. Value contains text of first data node.
+        node_data,          //!< A data node. Name is empty. Value contains data text.
+        node_cdata,         //!< A CDATA node. Name is empty. Value contains data text.
+        node_comment,       //!< A comment node. Name is empty. Value contains comment text.
+        node_declaration,   //!< A declaration node. Name and value are empty. Declaration parameters (version, encoding and standalone) are in node attributes.
+        node_doctype,       //!< A DOCTYPE node. Name is empty. Value contains DOCTYPE text.
+        node_pi             //!< A PI node. Name contains target. Value contains instructions.
+    };
+
+    ///////////////////////////////////////////////////////////////////////
+    // Parsing flags
+
+    //! Parse flag instructing the parser to not create data nodes. 
+    //! Text of first data node will still be placed in value of parent element, unless rapidxml::parse_no_element_values flag is also specified.
+    //! Can be combined with other flags by use of | operator.
+    //! <br><br>
+    //! See xml_document::parse() function.
+    const int parse_no_data_nodes = 0x1;            
+
+    //! Parse flag instructing the parser to not use text of first data node as a value of parent element.
+    //! Can be combined with other flags by use of | operator.
+    //! Note that child data nodes of element node take precendence over its value when printing. 
+    //! That is, if element has one or more child data nodes <em>and</em> a value, the value will be ignored.
+    //! Use rapidxml::parse_no_data_nodes flag to prevent creation of data nodes if you want to manipulate data using values of elements.
+    //! <br><br>
+    //! See xml_document::parse() function.
+    const int parse_no_element_values = 0x2;
+    
+    //! Parse flag instructing the parser to not place zero terminators after strings in the source text.
+    //! By default zero terminators are placed, modifying source text.
+    //! Can be combined with other flags by use of | operator.
+    //! <br><br>
+    //! See xml_document::parse() function.
+    const int parse_no_string_terminators = 0x4;
+    
+    //! Parse flag instructing the parser to not translate entities in the source text.
+    //! By default entities are translated, modifying source text.
+    //! Can be combined with other flags by use of | operator.
+    //! <br><br>
+    //! See xml_document::parse() function.
+    const int parse_no_entity_translation = 0x8;
+    
+    //! Parse flag instructing the parser to disable UTF-8 handling and assume plain 8 bit characters.
+    //! By default, UTF-8 handling is enabled.
+    //! Can be combined with other flags by use of | operator.
+    //! <br><br>
+    //! See xml_document::parse() function.
+    const int parse_no_utf8 = 0x10;
+    
+    //! Parse flag instructing the parser to create XML declaration node.
+    //! By default, declaration node is not created.
+    //! Can be combined with other flags by use of | operator.
+    //! <br><br>
+    //! See xml_document::parse() function.
+    const int parse_declaration_node = 0x20;
+    
+    //! Parse flag instructing the parser to create comments nodes.
+    //! By default, comment nodes are not created.
+    //! Can be combined with other flags by use of | operator.
+    //! <br><br>
+    //! See xml_document::parse() function.
+    const int parse_comment_nodes = 0x40;
+    
+    //! Parse flag instructing the parser to create DOCTYPE node.
+    //! By default, doctype node is not created.
+    //! Although W3C specification allows at most one DOCTYPE node, RapidXml will silently accept documents with more than one.
+    //! Can be combined with other flags by use of | operator.
+    //! <br><br>
+    //! See xml_document::parse() function.
+    const int parse_doctype_node = 0x80;
+    
+    //! Parse flag instructing the parser to create PI nodes.
+    //! By default, PI nodes are not created.
+    //! Can be combined with other flags by use of | operator.
+    //! <br><br>
+    //! See xml_document::parse() function.
+    const int parse_pi_nodes = 0x100;
+    
+    //! Parse flag instructing the parser to validate closing tag names. 
+    //! If not set, name inside closing tag is irrelevant to the parser.
+    //! By default, closing tags are not validated.
+    //! Can be combined with other flags by use of | operator.
+    //! <br><br>
+    //! See xml_document::parse() function.
+    const int parse_validate_closing_tags = 0x200;
+    
+    //! Parse flag instructing the parser to trim all leading and trailing whitespace of data nodes.
+    //! By default, whitespace is not trimmed. 
+    //! This flag does not cause the parser to modify source text.
+    //! Can be combined with other flags by use of | operator.
+    //! <br><br>
+    //! See xml_document::parse() function.
+    const int parse_trim_whitespace = 0x400;
+
+    //! Parse flag instructing the parser to condense all whitespace runs of data nodes to a single space character.
+    //! Trimming of leading and trailing whitespace of data is controlled by rapidxml::parse_trim_whitespace flag.
+    //! By default, whitespace is not normalized. 
+    //! If this flag is specified, source text will be modified.
+    //! Can be combined with other flags by use of | operator.
+    //! <br><br>
+    //! See xml_document::parse() function.
+    const int parse_normalize_whitespace = 0x800;
+
+    // Compound flags
+    
+    //! Parse flags which represent default behaviour of the parser. 
+    //! This is always equal to 0, so that all other flags can be simply ored together.
+    //! Normally there is no need to inconveniently disable flags by anding with their negated (~) values.
+    //! This also means that meaning of each flag is a <i>negation</i> of the default setting. 
+    //! For example, if flag name is rapidxml::parse_no_utf8, it means that utf-8 is <i>enabled</i> by default,
+    //! and using the flag will disable it.
+    //! <br><br>
+    //! See xml_document::parse() function.
+    const int parse_default = 0;
+    
+    //! A combination of parse flags that forbids any modifications of the source text. 
+    //! This also results in faster parsing. However, note that the following will occur:
+    //! <ul>
+    //! <li>names and values of nodes will not be zero terminated, you have to use xml_base::name_size() and xml_base::value_size() functions to determine where name and value ends</li>
+    //! <li>entities will not be translated</li>
+    //! <li>whitespace will not be normalized</li>
+    //! </ul>
+    //! See xml_document::parse() function.
+    const int parse_non_destructive = parse_no_string_terminators | parse_no_entity_translation;
+    
+    //! A combination of parse flags resulting in fastest possible parsing, without sacrificing important data.
+    //! <br><br>
+    //! See xml_document::parse() function.
+    const int parse_fastest = parse_non_destructive | parse_no_data_nodes;
+    
+    //! A combination of parse flags resulting in largest amount of data being extracted. 
+    //! This usually results in slowest parsing.
+    //! <br><br>
+    //! See xml_document::parse() function.
+    const int parse_full = parse_declaration_node | parse_comment_nodes | parse_doctype_node | parse_pi_nodes | parse_validate_closing_tags;
+
+    ///////////////////////////////////////////////////////////////////////
+    // Internals
+
+    //! \cond internal
+    namespace internal
+    {
+
+        // Struct that contains lookup tables for the parser
+        // It must be a template to allow correct linking (because it has static data members, which are defined in a header file).
+        template<int Dummy>
+        struct lookup_tables
+        {
+            static const unsigned char lookup_whitespace[256];              // Whitespace table
+            static const unsigned char lookup_node_name[256];               // Node name table
+            static const unsigned char lookup_text[256];                    // Text table
+            static const unsigned char lookup_text_pure_no_ws[256];         // Text table
+            static const unsigned char lookup_text_pure_with_ws[256];       // Text table
+            static const unsigned char lookup_attribute_name[256];          // Attribute name table
+            static const unsigned char lookup_attribute_data_1[256];        // Attribute data table with single quote
+            static const unsigned char lookup_attribute_data_1_pure[256];   // Attribute data table with single quote
+            static const unsigned char lookup_attribute_data_2[256];        // Attribute data table with double quotes
+            static const unsigned char lookup_attribute_data_2_pure[256];   // Attribute data table with double quotes
+            static const unsigned char lookup_digits[256];                  // Digits
+            static const unsigned char lookup_upcase[256];                  // To uppercase conversion table for ASCII characters
+        };
+
+        // Find length of the string
+        template<class Ch>
+        inline std::size_t measure(const Ch *p)
+        {
+            const Ch *tmp = p;
+            while (*tmp) 
+                ++tmp;
+            return tmp - p;
+        }
+
+        // Compare strings for equality
+        template<class Ch>
+        inline bool compare(const Ch *p1, std::size_t size1, const Ch *p2, std::size_t size2, bool case_sensitive)
+        {
+            if (size1 != size2)
+                return false;
+            if (case_sensitive)
+            {
+                for (const Ch *end = p1 + size1; p1 < end; ++p1, ++p2)
+                    if (*p1 != *p2)
+                        return false;
+            }
+            else
+            {
+                for (const Ch *end = p1 + size1; p1 < end; ++p1, ++p2)
+                    if (lookup_tables<0>::lookup_upcase[static_cast<unsigned char>(*p1)] != lookup_tables<0>::lookup_upcase[static_cast<unsigned char>(*p2)])
+                        return false;
+            }
+            return true;
+        }
+    }
+    //! \endcond
+
+    ///////////////////////////////////////////////////////////////////////
+    // Memory pool
+    
+    //! This class is used by the parser to create new nodes and attributes, without overheads of dynamic memory allocation.
+    //! In most cases, you will not need to use this class directly. 
+    //! However, if you need to create nodes manually or modify names/values of nodes, 
+    //! you are encouraged to use memory_pool of relevant xml_document to allocate the memory. 
+    //! Not only is this faster than allocating them by using <code>new</code> operator, 
+    //! but also their lifetime will be tied to the lifetime of document, 
+    //! possibly simplyfing memory management. 
+    //! <br><br>
+    //! Call allocate_node() or allocate_attribute() functions to obtain new nodes or attributes from the pool. 
+    //! You can also call allocate_string() function to allocate strings.
+    //! Such strings can then be used as names or values of nodes without worrying about their lifetime.
+    //! Note that there is no <code>free()</code> function -- all allocations are freed at once when clear() function is called, 
+    //! or when the pool is destroyed.
+    //! <br><br>
+    //! It is also possible to create a standalone memory_pool, and use it 
+    //! to allocate nodes, whose lifetime will not be tied to any document.
+    //! <br><br>
+    //! Pool maintains <code>RAPIDXML_STATIC_POOL_SIZE</code> bytes of statically allocated memory. 
+    //! Until static memory is exhausted, no dynamic memory allocations are done.
+    //! When static memory is exhausted, pool allocates additional blocks of memory of size <code>RAPIDXML_DYNAMIC_POOL_SIZE</code> each,
+    //! by using global <code>new[]</code> and <code>delete[]</code> operators. 
+    //! This behaviour can be changed by setting custom allocation routines. 
+    //! Use set_allocator() function to set them.
+    //! <br><br>
+    //! Allocations for nodes, attributes and strings are aligned at <code>RAPIDXML_ALIGNMENT</code> bytes.
+    //! This value defaults to the size of pointer on target architecture.
+    //! <br><br>
+    //! To obtain absolutely top performance from the parser,
+    //! it is important that all nodes are allocated from a single, contiguous block of memory.
+    //! Otherwise, cache misses when jumping between two (or more) disjoint blocks of memory can slow down parsing quite considerably.
+    //! If required, you can tweak <code>RAPIDXML_STATIC_POOL_SIZE</code>, <code>RAPIDXML_DYNAMIC_POOL_SIZE</code> and <code>RAPIDXML_ALIGNMENT</code> 
+    //! to obtain best wasted memory to performance compromise.
+    //! To do it, define their values before rapidxml.hpp file is included.
+    //! \param Ch Character type of created nodes. 
+    template<class Ch = char>
+    class memory_pool
+    {
+        
+    public:
+
+        //! \cond internal
+        typedef void *(alloc_func)(std::size_t);       // Type of user-defined function used to allocate memory
+        typedef void (free_func)(void *);              // Type of user-defined function used to free memory
+        //! \endcond
+        
+        //! Constructs empty pool with default allocator functions.
+        memory_pool()
+            : m_alloc_func(0)
+            , m_free_func(0)
+        {
+            init();
+        }
+
+        //! Destroys pool and frees all the memory. 
+        //! This causes memory occupied by nodes allocated by the pool to be freed.
+        //! Nodes allocated from the pool are no longer valid.
+        ~memory_pool()
+        {
+            clear();
+        }
+
+        //! Allocates a new node from the pool, and optionally assigns name and value to it. 
+        //! If the allocation request cannot be accomodated, this function will throw <code>std::bad_alloc</code>.
+        //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function
+        //! will call rapidxml::parse_error_handler() function.
+        //! \param type Type of node to create.
+        //! \param name Name to assign to the node, or 0 to assign no name.
+        //! \param value Value to assign to the node, or 0 to assign no value.
+        //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string.
+        //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string.
+        //! \return Pointer to allocated node. This pointer will never be NULL.
+        xml_node<Ch> *allocate_node(node_type type, 
+                                    const Ch *name = 0, const Ch *value = 0, 
+                                    std::size_t name_size = 0, std::size_t value_size = 0)
+        {
+            void *memory = allocate_aligned(sizeof(xml_node<Ch>));
+            xml_node<Ch> *node = new(memory) xml_node<Ch>(type);
+            if (name)
+            {
+                if (name_size > 0)
+                    node->name(name, name_size);
+                else
+                    node->name(name);
+            }
+            if (value)
+            {
+                if (value_size > 0)
+                    node->value(value, value_size);
+                else
+                    node->value(value);
+            }
+            return node;
+        }
+
+        //! Allocates a new attribute from the pool, and optionally assigns name and value to it.
+        //! If the allocation request cannot be accomodated, this function will throw <code>std::bad_alloc</code>.
+        //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function
+        //! will call rapidxml::parse_error_handler() function.
+        //! \param name Name to assign to the attribute, or 0 to assign no name.
+        //! \param value Value to assign to the attribute, or 0 to assign no value.
+        //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string.
+        //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string.
+        //! \return Pointer to allocated attribute. This pointer will never be NULL.
+        xml_attribute<Ch> *allocate_attribute(const Ch *name = 0, const Ch *value = 0, 
+                                              std::size_t name_size = 0, std::size_t value_size = 0)
+        {
+            void *memory = allocate_aligned(sizeof(xml_attribute<Ch>));
+            xml_attribute<Ch> *attribute = new(memory) xml_attribute<Ch>;
+            if (name)
+            {
+                if (name_size > 0)
+                    attribute->name(name, name_size);
+                else
+                    attribute->name(name);
+            }
+            if (value)
+            {
+                if (value_size > 0)
+                    attribute->value(value, value_size);
+                else
+                    attribute->value(value);
+            }
+            return attribute;
+        }
+
+        //! Allocates a char array of given size from the pool, and optionally copies a given string to it.
+        //! If the allocation request cannot be accomodated, this function will throw <code>std::bad_alloc</code>.
+        //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function
+        //! will call rapidxml::parse_error_handler() function.
+        //! \param source String to initialize the allocated memory with, or 0 to not initialize it.
+        //! \param size Number of characters to allocate, or zero to calculate it automatically from source string length; if size is 0, source string must be specified and null terminated.
+        //! \return Pointer to allocated char array. This pointer will never be NULL.
+        Ch *allocate_string(const Ch *source = 0, std::size_t size = 0)
+        {
+            assert(source || size);     // Either source or size (or both) must be specified
+            if (size == 0)
+                size = internal::measure(source) + 1;
+            Ch *result = static_cast<Ch *>(allocate_aligned(size * sizeof(Ch)));
+            if (source)
+                for (std::size_t i = 0; i < size; ++i)
+                    result[i] = source[i];
+            return result;
+        }
+
+        //! Clones an xml_node and its hierarchy of child nodes and attributes.
+        //! Nodes and attributes are allocated from this memory pool.
+        //! Names and values are not cloned, they are shared between the clone and the source.
+        //! Result node can be optionally specified as a second parameter, 
+        //! in which case its contents will be replaced with cloned source node.
+        //! This is useful when you want to clone entire document.
+        //! \param source Node to clone.
+        //! \param result Node to put results in, or 0 to automatically allocate result node
+        //! \return Pointer to cloned node. This pointer will never be NULL.
+        xml_node<Ch> *clone_node(const xml_node<Ch> *source, xml_node<Ch> *result = 0)
+        {
+            // Prepare result node
+            if (result)
+            {
+                result->remove_all_attributes();
+                result->remove_all_nodes();
+                result->type(source->type());
+            }
+            else
+                result = allocate_node(source->type());
+
+            // Clone name and value
+            result->name(source->name(), source->name_size());
+            result->value(source->value(), source->value_size());
+
+            // Clone child nodes and attributes
+            for (xml_node<Ch> *child = source->first_node(); child; child = child->next_sibling())
+                result->append_node(clone_node(child));
+            for (xml_attribute<Ch> *attr = source->first_attribute(); attr; attr = attr->next_attribute())
+                result->append_attribute(allocate_attribute(attr->name(), attr->value(), attr->name_size(), attr->value_size()));
+
+            return result;
+        }
+
+        //! Clears the pool. 
+        //! This causes memory occupied by nodes allocated by the pool to be freed.
+        //! Any nodes or strings allocated from the pool will no longer be valid.
+        void clear()
+        {
+            while (m_begin != m_static_memory)
+            {
+                char *previous_begin = reinterpret_cast<header *>(align(m_begin))->previous_begin;
+                if (m_free_func)
+                    m_free_func(m_begin);
+                else
+                    delete[] m_begin;
+                m_begin = previous_begin;
+            }
+            init();
+        }
+
+        //! Sets or resets the user-defined memory allocation functions for the pool.
+        //! This can only be called when no memory is allocated from the pool yet, otherwise results are undefined.
+        //! Allocation function must not return invalid pointer on failure. It should either throw,
+        //! stop the program, or use <code>longjmp()</code> function to pass control to other place of program. 
+        //! If it returns invalid pointer, results are undefined.
+        //! <br><br>
+        //! User defined allocation functions must have the following forms:
+        //! <br><code>
+        //! <br>void *allocate(std::size_t size);
+        //! <br>void free(void *pointer);
+        //! </code><br>
+        //! \param af Allocation function, or 0 to restore default function
+        //! \param ff Free function, or 0 to restore default function
+        void set_allocator(alloc_func *af, free_func *ff)
+        {
+            assert(m_begin == m_static_memory && m_ptr == align(m_begin));    // Verify that no memory is allocated yet
+            m_alloc_func = af;
+            m_free_func = ff;
+        }
+
+    private:
+
+        struct header
+        {
+            char *previous_begin;
+        };
+
+        void init()
+        {
+            m_begin = m_static_memory;
+            m_ptr = align(m_begin);
+            m_end = m_static_memory + sizeof(m_static_memory);
+        }
+        
+        char *align(char *ptr)
+        {
+            std::size_t alignment = ((RAPIDXML_ALIGNMENT - (std::size_t(ptr) & (RAPIDXML_ALIGNMENT - 1))) & (RAPIDXML_ALIGNMENT - 1));
+            return ptr + alignment;
+        }
+        
+        char *allocate_raw(std::size_t size)
+        {
+            // Allocate
+            void *memory;   
+            if (m_alloc_func)   // Allocate memory using either user-specified allocation function or global operator new[]
+            {
+                memory = m_alloc_func(size);
+                assert(memory); // Allocator is not allowed to return 0, on failure it must either throw, stop the program or use longjmp
+            }
+            else
+            {
+                memory = new char[size];
+#ifdef RAPIDXML_NO_EXCEPTIONS
+                if (!memory)            // If exceptions are disabled, verify memory allocation, because new will not be able to throw bad_alloc
+                    RAPIDXML_PARSE_ERROR("out of memory", 0);
+#endif
+            }
+            return static_cast<char *>(memory);
+        }
+        
+        void *allocate_aligned(std::size_t size)
+        {
+            // Calculate aligned pointer
+            char *result = align(m_ptr);
+
+            // If not enough memory left in current pool, allocate a new pool
+            if (result + size > m_end)
+            {
+                // Calculate required pool size (may be bigger than RAPIDXML_DYNAMIC_POOL_SIZE)
+                std::size_t pool_size = RAPIDXML_DYNAMIC_POOL_SIZE;
+                if (pool_size < size)
+                    pool_size = size;
+                
+                // Allocate
+                std::size_t alloc_size = sizeof(header) + (2 * RAPIDXML_ALIGNMENT - 2) + pool_size;     // 2 alignments required in worst case: one for header, one for actual allocation
+                char *raw_memory = allocate_raw(alloc_size);
+                    
+                // Setup new pool in allocated memory
+                char *pool = align(raw_memory);
+                header *new_header = reinterpret_cast<header *>(pool);
+                new_header->previous_begin = m_begin;
+                m_begin = raw_memory;
+                m_ptr = pool + sizeof(header);
+                m_end = raw_memory + alloc_size;
+
+                // Calculate aligned pointer again using new pool
+                result = align(m_ptr);
+            }
+
+            // Update pool and return aligned pointer
+            m_ptr = result + size;
+            return result;
+        }
+
+        char *m_begin;                                      // Start of raw memory making up current pool
+        char *m_ptr;                                        // First free byte in current pool
+        char *m_end;                                        // One past last available byte in current pool
+        char m_static_memory[RAPIDXML_STATIC_POOL_SIZE];    // Static raw memory
+        alloc_func *m_alloc_func;                           // Allocator function, or 0 if default is to be used
+        free_func *m_free_func;                             // Free function, or 0 if default is to be used
+    };
+
+    ///////////////////////////////////////////////////////////////////////////
+    // XML base
+
+    //! Base class for xml_node and xml_attribute implementing common functions: 
+    //! name(), name_size(), value(), value_size() and parent().
+    //! \param Ch Character type to use
+    template<class Ch = char>
+    class xml_base
+    {
+
+    public:
+        
+        ///////////////////////////////////////////////////////////////////////////
+        // Construction & destruction
+    
+        // Construct a base with empty name, value and parent
+        xml_base()
+            : m_name(0)
+            , m_value(0)
+            , m_parent(0)
+        {
+        }
+
+        ///////////////////////////////////////////////////////////////////////////
+        // Node data access
+    
+        //! Gets name of the node. 
+        //! Interpretation of name depends on type of node.
+        //! Note that name will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse.
+        //! <br><br>
+        //! Use name_size() function to determine length of the name.
+        //! \return Name of node, or empty string if node has no name.
+        Ch *name() const
+        {
+            return m_name ? m_name : nullstr();
+        }
+
+        //! Gets size of node name, not including terminator character.
+        //! This function works correctly irrespective of whether name is or is not zero terminated.
+        //! \return Size of node name, in characters.
+        std::size_t name_size() const
+        {
+            return m_name ? m_name_size : 0;
+        }
+
+        //! Gets value of node. 
+        //! Interpretation of value depends on type of node.
+        //! Note that value will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse.
+        //! <br><br>
+        //! Use value_size() function to determine length of the value.
+        //! \return Value of node, or empty string if node has no value.
+        Ch *value() const
+        {
+            return m_value ? m_value : nullstr();
+        }
+
+        //! Gets size of node value, not including terminator character.
+        //! This function works correctly irrespective of whether value is or is not zero terminated.
+        //! \return Size of node value, in characters.
+        std::size_t value_size() const
+        {
+            return m_value ? m_value_size : 0;
+        }
+
+        ///////////////////////////////////////////////////////////////////////////
+        // Node modification
+    
+        //! Sets name of node to a non zero-terminated string.
+        //! See \ref ownership_of_strings.
+        //! <br><br>
+        //! Note that node does not own its name or value, it only stores a pointer to it. 
+        //! It will not delete or otherwise free the pointer on destruction.
+        //! It is reponsibility of the user to properly manage lifetime of the string.
+        //! The easiest way to achieve it is to use memory_pool of the document to allocate the string -
+        //! on destruction of the document the string will be automatically freed.
+        //! <br><br>
+        //! Size of name must be specified separately, because name does not have to be zero terminated.
+        //! Use name(const Ch *) function to have the length automatically calculated (string must be zero terminated).
+        //! \param name Name of node to set. Does not have to be zero terminated.
+        //! \param size Size of name, in characters. This does not include zero terminator, if one is present.
+        void name(const Ch *name, std::size_t size)
+        {
+            m_name = const_cast<Ch *>(name);
+            m_name_size = size;
+        }
+
+        //! Sets name of node to a zero-terminated string.
+        //! See also \ref ownership_of_strings and xml_node::name(const Ch *, std::size_t).
+        //! \param name Name of node to set. Must be zero terminated.
+        void name(const Ch *name)
+        {
+            this->name(name, internal::measure(name));
+        }
+
+        //! Sets value of node to a non zero-terminated string.
+        //! See \ref ownership_of_strings.
+        //! <br><br>
+        //! Note that node does not own its name or value, it only stores a pointer to it. 
+        //! It will not delete or otherwise free the pointer on destruction.
+        //! It is reponsibility of the user to properly manage lifetime of the string.
+        //! The easiest way to achieve it is to use memory_pool of the document to allocate the string -
+        //! on destruction of the document the string will be automatically freed.
+        //! <br><br>
+        //! Size of value must be specified separately, because it does not have to be zero terminated.
+        //! Use value(const Ch *) function to have the length automatically calculated (string must be zero terminated).
+        //! <br><br>
+        //! If an element has a child node of type node_data, it will take precedence over element value when printing.
+        //! If you want to manipulate data of elements using values, use parser flag rapidxml::parse_no_data_nodes to prevent creation of data nodes by the parser.
+        //! \param value value of node to set. Does not have to be zero terminated.
+        //! \param size Size of value, in characters. This does not include zero terminator, if one is present.
+        void value(const Ch *value, std::size_t size)
+        {
+            m_value = const_cast<Ch *>(value);
+            m_value_size = size;
+        }
+
+        //! Sets value of node to a zero-terminated string.
+        //! See also \ref ownership_of_strings and xml_node::value(const Ch *, std::size_t).
+        //! \param value Vame of node to set. Must be zero terminated.
+        void value(const Ch *value)
+        {
+            this->value(value, internal::measure(value));
+        }
+
+        ///////////////////////////////////////////////////////////////////////////
+        // Related nodes access
+    
+        //! Gets node parent.
+        //! \return Pointer to parent node, or 0 if there is no parent.
+        xml_node<Ch> *parent() const
+        {
+            return m_parent;
+        }
+
+    protected:
+
+        // Return empty string
+        static Ch *nullstr()
+        {
+            static Ch zero = Ch('\0');
+            return &zero;
+        }
+
+        Ch *m_name;                         // Name of node, or 0 if no name
+        Ch *m_value;                        // Value of node, or 0 if no value
+        std::size_t m_name_size;            // Length of node name, or undefined of no name
+        std::size_t m_value_size;           // Length of node value, or undefined if no value
+        xml_node<Ch> *m_parent;             // Pointer to parent node, or 0 if none
+
+    };
+
+    //! Class representing attribute node of XML document. 
+    //! Each attribute has name and value strings, which are available through name() and value() functions (inherited from xml_base).
+    //! Note that after parse, both name and value of attribute will point to interior of source text used for parsing. 
+    //! Thus, this text must persist in memory for the lifetime of attribute.
+    //! \param Ch Character type to use.
+    template<class Ch = char>
+    class xml_attribute: public xml_base<Ch>
+    {
+
+        friend class xml_node<Ch>;
+    
+    public:
+
+        ///////////////////////////////////////////////////////////////////////////
+        // Construction & destruction
+    
+        //! Constructs an empty attribute with the specified type. 
+        //! Consider using memory_pool of appropriate xml_document if allocating attributes manually.
+        xml_attribute()
+        {
+        }
+
+        ///////////////////////////////////////////////////////////////////////////
+        // Related nodes access
+    
+        //! Gets document of which attribute is a child.
+        //! \return Pointer to document that contains this attribute, or 0 if there is no parent document.
+        xml_document<Ch> *document() const
+        {
+            if (xml_node<Ch> *node = this->parent())
+            {
+                while (node->parent())
+                    node = node->parent();
+                return node->type() == node_document ? static_cast<xml_document<Ch> *>(node) : 0;
+            }
+            else
+                return 0;
+        }
+
+        //! Gets previous attribute, optionally matching attribute name. 
+        //! \param name Name of attribute to find, or 0 to return previous attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
+        //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string
+        //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters
+        //! \return Pointer to found attribute, or 0 if not found.
+        xml_attribute<Ch> *previous_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const
+        {
+            if (name)
+            {
+                if (name_size == 0)
+                    name_size = internal::measure(name);
+                for (xml_attribute<Ch> *attribute = m_prev_attribute; attribute; attribute = attribute->m_prev_attribute)
+                    if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive))
+                        return attribute;
+                return 0;
+            }
+            else
+                return this->m_parent ? m_prev_attribute : 0;
+        }
+
+        //! Gets next attribute, optionally matching attribute name. 
+        //! \param name Name of attribute to find, or 0 to return next attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
+        //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string
+        //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters
+        //! \return Pointer to found attribute, or 0 if not found.
+        xml_attribute<Ch> *next_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const
+        {
+            if (name)
+            {
+                if (name_size == 0)
+                    name_size = internal::measure(name);
+                for (xml_attribute<Ch> *attribute = m_next_attribute; attribute; attribute = attribute->m_next_attribute)
+                    if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive))
+                        return attribute;
+                return 0;
+            }
+            else
+                return this->m_parent ? m_next_attribute : 0;
+        }
+
+    private:
+
+        xml_attribute<Ch> *m_prev_attribute;        // Pointer to previous sibling of attribute, or 0 if none; only valid if parent is non-zero
+        xml_attribute<Ch> *m_next_attribute;        // Pointer to next sibling of attribute, or 0 if none; only valid if parent is non-zero
+    
+    };
+
+    ///////////////////////////////////////////////////////////////////////////
+    // XML node
+
+    //! Class representing a node of XML document. 
+    //! Each node may have associated name and value strings, which are available through name() and value() functions. 
+    //! Interpretation of name and value depends on type of the node.
+    //! Type of node can be determined by using type() function.
+    //! <br><br>
+    //! Note that after parse, both name and value of node, if any, will point interior of source text used for parsing. 
+    //! Thus, this text must persist in the memory for the lifetime of node.
+    //! \param Ch Character type to use.
+    template<class Ch = char>
+    class xml_node: public xml_base<Ch>
+    {
+
+    public:
+
+        ///////////////////////////////////////////////////////////////////////////
+        // Construction & destruction
+    
+        //! Constructs an empty node with the specified type. 
+        //! Consider using memory_pool of appropriate document to allocate nodes manually.
+        //! \param type Type of node to construct.
+        xml_node(node_type type)
+            : m_type(type)
+            , m_first_node(0)
+            , m_first_attribute(0)
+        {
+        }
+
+        ///////////////////////////////////////////////////////////////////////////
+        // Node data access
+    
+        //! Gets type of node.
+        //! \return Type of node.
+        node_type type() const
+        {
+            return m_type;
+        }
+
+        ///////////////////////////////////////////////////////////////////////////
+        // Related nodes access
+    
+        //! Gets document of which node is a child.
+        //! \return Pointer to document that contains this node, or 0 if there is no parent document.
+        xml_document<Ch> *document() const
+        {
+            xml_node<Ch> *node = const_cast<xml_node<Ch> *>(this);
+            while (node->parent())
+                node = node->parent();
+            return node->type() == node_document ? static_cast<xml_document<Ch> *>(node) : 0;
+        }
+
+        //! Gets first child node, optionally matching node name.
+        //! \param name Name of child to find, or 0 to return first child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
+        //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string
+        //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters
+        //! \return Pointer to found child, or 0 if not found.
+        xml_node<Ch> *first_node(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const
+        {
+            if (name)
+            {
+                if (name_size == 0)
+                    name_size = internal::measure(name);
+                for (xml_node<Ch> *child = m_first_node; child; child = child->next_sibling())
+                    if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive))
+                        return child;
+                return 0;
+            }
+            else
+                return m_first_node;
+        }
+
+        //! Gets last child node, optionally matching node name. 
+        //! Behaviour is undefined if node has no children.
+        //! Use first_node() to test if node has children.
+        //! \param name Name of child to find, or 0 to return last child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
+        //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string
+        //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters
+        //! \return Pointer to found child, or 0 if not found.
+        xml_node<Ch> *last_node(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const
+        {
+            assert(m_first_node);  // Cannot query for last child if node has no children
+            if (name)
+            {
+                if (name_size == 0)
+                    name_size = internal::measure(name);
+                for (xml_node<Ch> *child = m_last_node; child; child = child->previous_sibling())
+                    if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive))
+                        return child;
+                return 0;
+            }
+            else
+                return m_last_node;
+        }
+
+        //! Gets previous sibling node, optionally matching node name. 
+        //! Behaviour is undefined if node has no parent.
+        //! Use parent() to test if node has a parent.
+        //! \param name Name of sibling to find, or 0 to return previous sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
+        //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string
+        //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters
+        //! \return Pointer to found sibling, or 0 if not found.
+        xml_node<Ch> *previous_sibling(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const
+        {
+            assert(this->m_parent);     // Cannot query for siblings if node has no parent
+            if (name)
+            {
+                if (name_size == 0)
+                    name_size = internal::measure(name);
+                for (xml_node<Ch> *sibling = m_prev_sibling; sibling; sibling = sibling->m_prev_sibling)
+                    if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive))
+                        return sibling;
+                return 0;
+            }
+            else
+                return m_prev_sibling;
+        }
+
+        //! Gets next sibling node, optionally matching node name. 
+        //! Behaviour is undefined if node has no parent.
+        //! Use parent() to test if node has a parent.
+        //! \param name Name of sibling to find, or 0 to return next sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
+        //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string
+        //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters
+        //! \return Pointer to found sibling, or 0 if not found.
+        xml_node<Ch> *next_sibling(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const
+        {
+            assert(this->m_parent);     // Cannot query for siblings if node has no parent
+            if (name)
+            {
+                if (name_size == 0)
+                    name_size = internal::measure(name);
+                for (xml_node<Ch> *sibling = m_next_sibling; sibling; sibling = sibling->m_next_sibling)
+                    if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive))
+                        return sibling;
+                return 0;
+            }
+            else
+                return m_next_sibling;
+        }
+
+        //! Gets first attribute of node, optionally matching attribute name.
+        //! \param name Name of attribute to find, or 0 to return first attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
+        //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string
+        //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters
+        //! \return Pointer to found attribute, or 0 if not found.
+        xml_attribute<Ch> *first_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const
+        {
+            if (name)
+            {
+                if (name_size == 0)
+                    name_size = internal::measure(name);
+                for (xml_attribute<Ch> *attribute = m_first_attribute; attribute; attribute = attribute->m_next_attribute)
+                    if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive))
+                        return attribute;
+                return 0;
+            }
+            else
+                return m_first_attribute;
+        }
+
+        //! Gets last attribute of node, optionally matching attribute name.
+        //! \param name Name of attribute to find, or 0 to return last attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
+        //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string
+        //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters
+        //! \return Pointer to found attribute, or 0 if not found.
+        xml_attribute<Ch> *last_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const
+        {
+            if (name)
+            {
+                if (name_size == 0)
+                    name_size = internal::measure(name);
+                for (xml_attribute<Ch> *attribute = m_last_attribute; attribute; attribute = attribute->m_prev_attribute)
+                    if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive))
+                        return attribute;
+                return 0;
+            }
+            else
+                return m_first_attribute ? m_last_attribute : 0;
+        }
+
+        ///////////////////////////////////////////////////////////////////////////
+        // Node modification
+    
+        //! Sets type of node.
+        //! \param type Type of node to set.
+        void type(node_type type)
+        {
+            m_type = type;
+        }
+
+        ///////////////////////////////////////////////////////////////////////////
+        // Node manipulation
+
+        //! Prepends a new child node.
+        //! The prepended child becomes the first child, and all existing children are moved one position back.
+        //! \param child Node to prepend.
+        void prepend_node(xml_node<Ch> *child)
+        {
+            assert(child && !child->parent() && child->type() != node_document);
+            if (first_node())
+            {
+                child->m_next_sibling = m_first_node;
+                m_first_node->m_prev_sibling = child;
+            }
+            else
+            {
+                child->m_next_sibling = 0;
+                m_last_node = child;
+            }
+            m_first_node = child;
+            child->m_parent = this;
+            child->m_prev_sibling = 0;
+        }
+
+        //! Appends a new child node. 
+        //! The appended child becomes the last child.
+        //! \param child Node to append.
+        void append_node(xml_node<Ch> *child)
+        {
+            assert(child && !child->parent() && child->type() != node_document);
+            if (first_node())
+            {
+                child->m_prev_sibling = m_last_node;
+                m_last_node->m_next_sibling = child;
+            }
+            else
+            {
+                child->m_prev_sibling = 0;
+                m_first_node = child;
+            }
+            m_last_node = child;
+            child->m_parent = this;
+            child->m_next_sibling = 0;
+        }
+
+        //! Inserts a new child node at specified place inside the node. 
+        //! All children after and including the specified node are moved one position back.
+        //! \param where Place where to insert the child, or 0 to insert at the back.
+        //! \param child Node to insert.
+        void insert_node(xml_node<Ch> *where, xml_node<Ch> *child)
+        {
+            assert(!where || where->parent() == this);
+            assert(child && !child->parent() && child->type() != node_document);
+            if (where == m_first_node)
+                prepend_node(child);
+            else if (where == 0)
+                append_node(child);
+            else
+            {
+                child->m_prev_sibling = where->m_prev_sibling;
+                child->m_next_sibling = where;
+                where->m_prev_sibling->m_next_sibling = child;
+                where->m_prev_sibling = child;
+                child->m_parent = this;
+            }
+        }
+
+        //! Removes first child node. 
+        //! If node has no children, behaviour is undefined.
+        //! Use first_node() to test if node has children.
+        void remove_first_node()
+        {
+            assert(first_node());
+            xml_node<Ch> *child = m_first_node;
+            m_first_node = child->m_next_sibling;
+            if (child->m_next_sibling)
+                child->m_next_sibling->m_prev_sibling = 0;
+            else
+                m_last_node = 0;
+            child->m_parent = 0;
+        }
+
+        //! Removes last child of the node. 
+        //! If node has no children, behaviour is undefined.
+        //! Use first_node() to test if node has children.
+        void remove_last_node()
+        {
+            assert(first_node());
+            xml_node<Ch> *child = m_last_node;
+            if (child->m_prev_sibling)
+            {
+                m_last_node = child->m_prev_sibling;
+                child->m_prev_sibling->m_next_sibling = 0;
+            }
+            else
+                m_first_node = 0;