Roi Atalla avatar Roi Atalla committed 85a2f0a

So many changes! Finished porting Chapter 7 Example 1, but it doesn't exactly work yet. I may have mistyped something here and there. Also this included many changes to Matrix4, MatrixStack, Vector3, GLProgram, and even the pom.xml file (you only copied over *.vert and *.frag files, which gave me a nice lengthy debug session to figure out why my XML files aren't being copied -____-). I also created and fully functional Mesh class.

Comments (0)

Files changed (28)

 * Support for Maven in your IDE (Eclipse Juno and IntelliJ both come with maven support)
 
 * _Windows users:_ a version of the bash shell (either [Cygwin](http://cygwin.com)
-  or [Git Bash](https://openhatch.org/missions/windows-setup/install-git-bash) are fine)
+  or [Git Bash](http://git-scm.com/downloads) are fine)
 
 ### How to run samples ###
 
 Under each section you can find another README file that gives more detail, and finally some of the samples themselves may have their own README.
 
 
-* **arcsynthesis:** Java ports of the excellent C tutorials from "Learning Modern 3d Graphics Programming"
+* **arcsynthesis:** Java ports of the excellent C++ tutorials from "Learning Modern 3d Graphics Programming"
 at [arcsynthesis.org](http://arcsynthesis.org/gltut)
 
 

Binary file added.

             <artifactId>vecmath</artifactId>
             <version>1.3.1</version>
         </dependency>
+        <dependency>
+        	<groupId>xpp3</groupId>
+        	<artifactId>xpp3</artifactId>
+        	<version>1.1.4c</version>
+        </dependency>
     </dependencies>
 
     <build>
                 <includes>
                     <include>**/*.frag</include>
                     <include>**/*.vert</include>
+                    <include>**/*.xml</include>
                 </includes>
             </resource>
         </resources> 

src/main/java/com/ra4king/opengl/GLProgram.java

 				Display.sync(fps);
 			}
 		}
-		catch(Exception exc) {
+		catch(Throwable exc) {
 			exc.printStackTrace();
 		}
 		finally {

src/main/java/com/ra4king/opengl/arcsynthesis/gl33/chapter5/example1/Example5_1.java

 			GREY_COLOR[0], GREY_COLOR[1], GREY_COLOR[2], GREY_COLOR[3],
 	};
 	
-	private final short[] indicies = {
+	private final short[] indices = {
 			0, 2, 1,
 			3, 2, 0,
 			
 		
 		int vbo2 = glGenBuffers();
 		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo2);
-		glBufferData(GL_ELEMENT_ARRAY_BUFFER, (ShortBuffer)BufferUtils.createShortBuffer(indicies.length).put(indicies).flip(), GL_STATIC_DRAW);
+		glBufferData(GL_ELEMENT_ARRAY_BUFFER, (ShortBuffer)BufferUtils.createShortBuffer(indices.length).put(indices).flip(), GL_STATIC_DRAW);
 		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
 		
 		vao1 = glGenVertexArrays();
 		
 		glBindVertexArray(vao1);
 		glUniform3f(offsetUniform, 0, 0, 0);
-		glDrawElements(GL_TRIANGLES, indicies.length, GL_UNSIGNED_SHORT, 0);
+		glDrawElements(GL_TRIANGLES, indices.length, GL_UNSIGNED_SHORT, 0);
 		
 		glBindVertexArray(vao2);
 		glUniform3f(offsetUniform, 0, 0, -1);
-		glDrawElements(GL_TRIANGLES, indicies.length, GL_UNSIGNED_SHORT, 0);
+		glDrawElements(GL_TRIANGLES, indices.length, GL_UNSIGNED_SHORT, 0);
 		
 		glBindVertexArray(0);
 		

src/main/java/com/ra4king/opengl/arcsynthesis/gl33/chapter5/example2/Example5_2.java

 			GREY_COLOR[0], GREY_COLOR[1], GREY_COLOR[2], GREY_COLOR[3],
 	};
 	
-	private final short[] indicies = {
+	private final short[] indices = {
 			0, 2, 1,
 			3, 2, 0,
 			
 		
 		int vbo2 = glGenBuffers();
 		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo2);
-		glBufferData(GL_ELEMENT_ARRAY_BUFFER, (ShortBuffer)BufferUtils.createShortBuffer(indicies.length).put(indicies).flip(), GL_STATIC_DRAW);
+		glBufferData(GL_ELEMENT_ARRAY_BUFFER, (ShortBuffer)BufferUtils.createShortBuffer(indices.length).put(indices).flip(), GL_STATIC_DRAW);
 		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
 		
 		vao = glGenVertexArrays();
 		
 		glBindVertexArray(vao);
 		glUniform3f(offsetUniform, 0, 0, 0);
-		glDrawElements(GL_TRIANGLES, indicies.length, GL_UNSIGNED_SHORT, 0);
+		glDrawElements(GL_TRIANGLES, indices.length, GL_UNSIGNED_SHORT, 0);
 		
 		glUniform3f(offsetUniform, 0, 0, -1);
-		glDrawElementsBaseVertex(GL_TRIANGLES, indicies.length, GL_UNSIGNED_SHORT, 0, 36/2);
+		glDrawElementsBaseVertex(GL_TRIANGLES, indices.length, GL_UNSIGNED_SHORT, 0, 36/2);
 		
 		glBindVertexArray(0);
 		

src/main/java/com/ra4king/opengl/arcsynthesis/gl33/chapter5/example3/Example5_3.java

 			GREY_COLOR[0], GREY_COLOR[1], GREY_COLOR[2], GREY_COLOR[3],
 	};
 	
-	private final short[] indicies = {
+	private final short[] indices = {
 			0, 2, 1,
 			3, 2, 0,
 			
 		
 		int vbo2 = glGenBuffers();
 		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo2);
-		glBufferData(GL_ELEMENT_ARRAY_BUFFER, (ShortBuffer)BufferUtils.createShortBuffer(indicies.length).put(indicies).flip(), GL_STATIC_DRAW);
+		glBufferData(GL_ELEMENT_ARRAY_BUFFER, (ShortBuffer)BufferUtils.createShortBuffer(indices.length).put(indices).flip(), GL_STATIC_DRAW);
 		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
 		
 		vao = glGenVertexArrays();
 		
 		glBindVertexArray(vao);
 		glUniform3f(offsetUniform, 0, 0, 0);
-		glDrawElements(GL_TRIANGLES, indicies.length, GL_UNSIGNED_SHORT, 0);
+		glDrawElements(GL_TRIANGLES, indices.length, GL_UNSIGNED_SHORT, 0);
 		
 		glUniform3f(offsetUniform, 0, 0, -1);
-		glDrawElementsBaseVertex(GL_TRIANGLES, indicies.length, GL_UNSIGNED_SHORT, 0, 36/2);
+		glDrawElementsBaseVertex(GL_TRIANGLES, indices.length, GL_UNSIGNED_SHORT, 0, 36/2);
 		
 		glBindVertexArray(0);
 		

src/main/java/com/ra4king/opengl/arcsynthesis/gl33/chapter5/example4/Example5_4.java

 			GREY_COLOR[0], GREY_COLOR[1], GREY_COLOR[2], GREY_COLOR[3],
 	};
 	
-	private final short[] indicies = {
+	private final short[] indices = {
 			0, 2, 1,
 			3, 2, 0,
 			
 		
 		int vbo2 = glGenBuffers();
 		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo2);
-		glBufferData(GL_ELEMENT_ARRAY_BUFFER, (ShortBuffer)BufferUtils.createShortBuffer(indicies.length).put(indicies).flip(), GL_STATIC_DRAW);
+		glBufferData(GL_ELEMENT_ARRAY_BUFFER, (ShortBuffer)BufferUtils.createShortBuffer(indices.length).put(indices).flip(), GL_STATIC_DRAW);
 		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
 		
 		vao = glGenVertexArrays();
 		
 		glBindVertexArray(vao);
 		glUniform3f(offsetUniform, 0, 0, 0.5f);
-		glDrawElements(GL_TRIANGLES, indicies.length, GL_UNSIGNED_SHORT, 0);
+		glDrawElements(GL_TRIANGLES, indices.length, GL_UNSIGNED_SHORT, 0);
 		
 		glUniform3f(offsetUniform, 0, 0, -1);
-		glDrawElementsBaseVertex(GL_TRIANGLES, indicies.length, GL_UNSIGNED_SHORT, 0, 36/2);
+		glDrawElementsBaseVertex(GL_TRIANGLES, indices.length, GL_UNSIGNED_SHORT, 0, 36/2);
 		
 		glBindVertexArray(0);
 		

src/main/java/com/ra4king/opengl/arcsynthesis/gl33/chapter5/example5/Example5_5.java

 			GREY_COLOR[0], GREY_COLOR[1], GREY_COLOR[2], GREY_COLOR[3],
 	};
 	
-	private final short[] indicies = {
+	private final short[] indices = {
 			0, 2, 1,
 			3, 2, 0,
 			
 		
 		int vbo2 = glGenBuffers();
 		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo2);
-		glBufferData(GL_ELEMENT_ARRAY_BUFFER, (ShortBuffer)BufferUtils.createShortBuffer(indicies.length).put(indicies).flip(), GL_STATIC_DRAW);
+		glBufferData(GL_ELEMENT_ARRAY_BUFFER, (ShortBuffer)BufferUtils.createShortBuffer(indices.length).put(indices).flip(), GL_STATIC_DRAW);
 		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
 		
 		vao = glGenVertexArrays();
 		
 		glBindVertexArray(vao);
 		glUniform3f(offsetUniform, 0, 0, 0.5f);
-		glDrawElements(GL_TRIANGLES, indicies.length, GL_UNSIGNED_SHORT, 0);
+		glDrawElements(GL_TRIANGLES, indices.length, GL_UNSIGNED_SHORT, 0);
 		
 		glUniform3f(offsetUniform, 0, 0, -1);
-		glDrawElementsBaseVertex(GL_TRIANGLES, indicies.length, GL_UNSIGNED_SHORT, 0, 36/2);
+		glDrawElementsBaseVertex(GL_TRIANGLES, indices.length, GL_UNSIGNED_SHORT, 0, 36/2);
 		
 		glBindVertexArray(0);
 		

src/main/java/com/ra4king/opengl/arcsynthesis/gl33/chapter6/example1/Example6_1.java

 			BROWN_COLOR[0], BROWN_COLOR[1], BROWN_COLOR[2], BROWN_COLOR[3],
 	};
 	
-	private final short[] indicies = {
+	private final short[] indices = {
 			0, 1, 2,
 			1, 0, 3,
 			2, 3, 0,
 		int vbo2 = glGenBuffers();
 		
 		glBindBuffer(GL_ARRAY_BUFFER, vbo2);
-		glBufferData(GL_ARRAY_BUFFER, (ShortBuffer)BufferUtils.createShortBuffer(indicies.length).put(indicies).flip(), GL_STATIC_DRAW);
+		glBufferData(GL_ARRAY_BUFFER, (ShortBuffer)BufferUtils.createShortBuffer(indices.length).put(indices).flip(), GL_STATIC_DRAW);
 		glBindBuffer(GL_ARRAY_BUFFER, 0);
 		
 		vao = glGenVertexArrays();
 		
 		for(Matrix4 m : modelToCameraMatrices) {
 			glUniformMatrix4(modelToCameraMatrixUniform, false, m.getBuffer());
-			glDrawElements(GL_TRIANGLES, indicies.length, GL_UNSIGNED_SHORT, 0);
+			glDrawElements(GL_TRIANGLES, indices.length, GL_UNSIGNED_SHORT, 0);
 		}
 		
 		glBindVertexArray(0);

src/main/java/com/ra4king/opengl/arcsynthesis/gl33/chapter6/example2/Example6_2.java

 			BROWN_COLOR[0], BROWN_COLOR[1], BROWN_COLOR[2], BROWN_COLOR[3],
 	};
 	
-	private final short[] indicies = {
+	private final short[] indices = {
 			0, 1, 2,
 			1, 0, 3,
 			2, 3, 0,
 		int vbo2 = glGenBuffers();
 		
 		glBindBuffer(GL_ARRAY_BUFFER, vbo2);
-		glBufferData(GL_ARRAY_BUFFER, (ShortBuffer)BufferUtils.createShortBuffer(indicies.length).put(indicies).flip(), GL_STATIC_DRAW);
+		glBufferData(GL_ARRAY_BUFFER, (ShortBuffer)BufferUtils.createShortBuffer(indices.length).put(indices).flip(), GL_STATIC_DRAW);
 		glBindBuffer(GL_ARRAY_BUFFER, 0);
 		
 		vao = glGenVertexArrays();
 		
 		for(Matrix4 m : modelToCameraMatrices) {
 			glUniformMatrix4(modelToCameraMatrixUniform, false, m.getBuffer());
-			glDrawElements(GL_TRIANGLES, indicies.length, GL_UNSIGNED_SHORT, 0);
+			glDrawElements(GL_TRIANGLES, indices.length, GL_UNSIGNED_SHORT, 0);
 		}
 		
 		glBindVertexArray(0);

src/main/java/com/ra4king/opengl/arcsynthesis/gl33/chapter6/example3/Example6_3.java

 			BROWN_COLOR[0], BROWN_COLOR[1], BROWN_COLOR[2], BROWN_COLOR[3],
 	};
 	
-	private final short[] indicies = {
+	private final short[] indices = {
 			0, 1, 2,
 			1, 0, 3,
 			2, 3, 0,
 		int vbo2 = glGenBuffers();
 		
 		glBindBuffer(GL_ARRAY_BUFFER, vbo2);
-		glBufferData(GL_ARRAY_BUFFER, (ShortBuffer)BufferUtils.createShortBuffer(indicies.length).put(indicies).flip(), GL_STATIC_DRAW);
+		glBufferData(GL_ARRAY_BUFFER, (ShortBuffer)BufferUtils.createShortBuffer(indices.length).put(indices).flip(), GL_STATIC_DRAW);
 		glBindBuffer(GL_ARRAY_BUFFER, 0);
 		
 		vao = glGenVertexArrays();
 		
 		for(Matrix4 m : modelToCameraMatrices) {
 			glUniformMatrix4(modelToCameraMatrixUniform, false, m.getBuffer());
-			glDrawElements(GL_TRIANGLES, indicies.length, GL_UNSIGNED_SHORT, 0);
+			glDrawElements(GL_TRIANGLES, indices.length, GL_UNSIGNED_SHORT, 0);
 		}
 		
 		glBindVertexArray(0);

src/main/java/com/ra4king/opengl/arcsynthesis/gl33/chapter6/example4/Example6_4.java

 		    MAGENTA_COLOR[0], MAGENTA_COLOR[1], MAGENTA_COLOR[2], MAGENTA_COLOR[3]
 	};
 	
-	private final short[] indicies = {
+	private final short[] indices = {
 			0, 1, 2,
 			2, 3, 0,
 			
 		
 		int vbo2 = glGenBuffers();
 		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo2);
-		glBufferData(GL_ELEMENT_ARRAY_BUFFER, (ShortBuffer)BufferUtils.createShortBuffer(indicies.length).put(indicies).flip(), GL_STATIC_DRAW);
+		glBufferData(GL_ELEMENT_ARRAY_BUFFER, (ShortBuffer)BufferUtils.createShortBuffer(indices.length).put(indices).flip(), GL_STATIC_DRAW);
 		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
 		
 		vao = glGenVertexArrays();
 			
 			glBindVertexArray(vao);
 			
-			modelToCameraStack.translate(posBase);
-			modelToCameraStack.rotateY(angleBase);
+			modelToCameraStack.getTop().translate(posBase);
+			modelToCameraStack.getTop().rotate(angleBase*(float)Math.PI/180,0,1,0);
 			
 			{
 				modelToCameraStack.pushMatrix();
-				modelToCameraStack.translate(posBaseLeft);
-				modelToCameraStack.scale(new Vector3(1,1,scaleBaseZ));
+				modelToCameraStack.getTop().translate(posBaseLeft);
+				modelToCameraStack.getTop().scale(new Vector3(1,1,scaleBaseZ));
 				glUniformMatrix4(modelToCameraMatrixUniform, false, modelToCameraStack.getTop().getBuffer());
-				glDrawElements(GL_TRIANGLES, indicies.length, GL_UNSIGNED_SHORT, 0);
+				glDrawElements(GL_TRIANGLES, indices.length, GL_UNSIGNED_SHORT, 0);
 				modelToCameraStack.popMatrix();
 			}
 			
 			{
 				modelToCameraStack.pushMatrix();
-				modelToCameraStack.translate(posBaseRight);
-				modelToCameraStack.scale(new Vector3(1,1,scaleBaseZ));
+				modelToCameraStack.getTop().translate(posBaseRight);
+				modelToCameraStack.getTop().scale(new Vector3(1,1,scaleBaseZ));
 				glUniformMatrix4(modelToCameraMatrixUniform, false, modelToCameraStack.getTop().getBuffer());
-				glDrawElements(GL_TRIANGLES, indicies.length, GL_UNSIGNED_SHORT, 0);
+				glDrawElements(GL_TRIANGLES, indices.length, GL_UNSIGNED_SHORT, 0);
 				modelToCameraStack.popMatrix();
 			}
 			
 		
 		private void drawFingers(MatrixStack modelToCameraStack) {
 			modelToCameraStack.pushMatrix();
-			modelToCameraStack.translate(posLeftFinger);
-			modelToCameraStack.rotateY(angleFingerOpen);
+			modelToCameraStack.getTop().translate(posLeftFinger);
+			modelToCameraStack.getTop().rotate(angleFingerOpen*(float)Math.PI/180,0,1,0);
 			
 			modelToCameraStack.pushMatrix();
-			modelToCameraStack.translate(new Vector3(0,0,lenFinger/2));
-			modelToCameraStack.scale(new Vector3(widthFinger/2,widthFinger/2,lenFinger/2));
+			modelToCameraStack.getTop().translate(new Vector3(0,0,lenFinger/2));
+			modelToCameraStack.getTop().scale(new Vector3(widthFinger/2,widthFinger/2,lenFinger/2));
 			glUniformMatrix4(modelToCameraMatrixUniform,false,modelToCameraStack.getTop().getBuffer());
-			glDrawElements(GL_TRIANGLES, indicies.length, GL_UNSIGNED_SHORT, 0);
+			glDrawElements(GL_TRIANGLES, indices.length, GL_UNSIGNED_SHORT, 0);
 			modelToCameraStack.popMatrix();
 			
 			{
 				modelToCameraStack.pushMatrix();
-				modelToCameraStack.translate(new Vector3(0,0,lenFinger));
-				modelToCameraStack.rotateY(-angleLowerFinger);
+				modelToCameraStack.getTop().translate(new Vector3(0,0,lenFinger));
+				modelToCameraStack.getTop().rotate(-angleLowerFinger*(float)Math.PI/180,0,1,0);
 				
 				modelToCameraStack.pushMatrix();
-				modelToCameraStack.translate(new Vector3(0,0,lenFinger/2));
-				modelToCameraStack.scale(new Vector3(widthFinger/2,widthFinger/2,lenFinger/2));
+				modelToCameraStack.getTop().translate(new Vector3(0,0,lenFinger/2));
+				modelToCameraStack.getTop().scale(new Vector3(widthFinger/2,widthFinger/2,lenFinger/2));
 				glUniformMatrix4(modelToCameraMatrixUniform,false,modelToCameraStack.getTop().getBuffer());
-				glDrawElements(GL_TRIANGLES, indicies.length, GL_UNSIGNED_SHORT, 0);
+				glDrawElements(GL_TRIANGLES, indices.length, GL_UNSIGNED_SHORT, 0);
 				modelToCameraStack.popMatrix();
 				
 				modelToCameraStack.popMatrix();
 			modelToCameraStack.popMatrix();
 			
 			modelToCameraStack.pushMatrix();
-			modelToCameraStack.translate(posRightFinger);
-			modelToCameraStack.rotateY(-angleFingerOpen);
+			modelToCameraStack.getTop().translate(posRightFinger);
+			modelToCameraStack.getTop().rotate(-angleFingerOpen*(float)Math.PI/180,0,1,0);
 			
 			modelToCameraStack.pushMatrix();
-			modelToCameraStack.translate(new Vector3(0,0,lenFinger/2));
-			modelToCameraStack.scale(new Vector3(widthFinger/2,widthFinger/2,lenFinger/2));
+			modelToCameraStack.getTop().translate(new Vector3(0,0,lenFinger/2));
+			modelToCameraStack.getTop().scale(new Vector3(widthFinger/2,widthFinger/2,lenFinger/2));
 			glUniformMatrix4(modelToCameraMatrixUniform,false,modelToCameraStack.getTop().getBuffer());
-			glDrawElements(GL_TRIANGLES, indicies.length, GL_UNSIGNED_SHORT, 0);
+			glDrawElements(GL_TRIANGLES, indices.length, GL_UNSIGNED_SHORT, 0);
 			modelToCameraStack.popMatrix();
 			
 			{
 				modelToCameraStack.pushMatrix();
-				modelToCameraStack.translate(new Vector3(0,0,lenFinger));
-				modelToCameraStack.rotateY(angleLowerFinger);
+				modelToCameraStack.getTop().translate(new Vector3(0,0,lenFinger));
+				modelToCameraStack.getTop().rotate(angleLowerFinger*(float)Math.PI/180,0,1,0);
 				
 				modelToCameraStack.pushMatrix();
-				modelToCameraStack.translate(new Vector3(0,0,lenFinger/2));
-				modelToCameraStack.scale(new Vector3(widthFinger/2,widthFinger/2,lenFinger/2));
+				modelToCameraStack.getTop().translate(new Vector3(0,0,lenFinger/2));
+				modelToCameraStack.getTop().scale(new Vector3(widthFinger/2,widthFinger/2,lenFinger/2));
 				glUniformMatrix4(modelToCameraMatrixUniform,false,modelToCameraStack.getTop().getBuffer());
-				glDrawElements(GL_TRIANGLES, indicies.length, GL_UNSIGNED_SHORT, 0);
+				glDrawElements(GL_TRIANGLES, indices.length, GL_UNSIGNED_SHORT, 0);
 				modelToCameraStack.popMatrix();
 				
 				modelToCameraStack.popMatrix();
 		
 		private void drawWrist(MatrixStack modelToCameraStack) {
 			modelToCameraStack.pushMatrix();
-			modelToCameraStack.translate(posWrist);
-			modelToCameraStack.rotateZ(angleWristRoll);
-			modelToCameraStack.rotateX(angleWristPitch);
+			modelToCameraStack.getTop().translate(posWrist);
+			modelToCameraStack.getTop().rotate(angleWristRoll*(float)Math.PI/180,0,0,1);
+			modelToCameraStack.getTop().rotate(angleWristPitch*(float)Math.PI/180,1,0,0);
 			
 			modelToCameraStack.pushMatrix();
-			modelToCameraStack.scale(new Vector3(widthWrist/2,widthWrist/2,lenWrist/2));
+			modelToCameraStack.getTop().scale(new Vector3(widthWrist/2,widthWrist/2,lenWrist/2));
 			glUniformMatrix4(modelToCameraMatrixUniform,false,modelToCameraStack.getTop().getBuffer());
-			glDrawElements(GL_TRIANGLES,indicies.length,GL_UNSIGNED_SHORT,0);
+			glDrawElements(GL_TRIANGLES,indices.length,GL_UNSIGNED_SHORT,0);
 			modelToCameraStack.popMatrix();
 			
 			drawFingers(modelToCameraStack);
 		
 		private void drawLowerArm(MatrixStack modelToCameraStack) {
 			modelToCameraStack.pushMatrix();
-			modelToCameraStack.translate(posLowerArm);
-			modelToCameraStack.rotateX(angleLowerArm);
+			modelToCameraStack.getTop().translate(posLowerArm);
+			modelToCameraStack.getTop().rotate(angleLowerArm*(float)Math.PI/180,1,0,0);
 			
 			modelToCameraStack.pushMatrix();
-			modelToCameraStack.translate(new Vector3(0,0,lenLowerArm/2));
-			modelToCameraStack.scale(new Vector3(widthLowerArm/2,widthLowerArm/2,lenLowerArm/2));
+			modelToCameraStack.getTop().translate(new Vector3(0,0,lenLowerArm/2));
+			modelToCameraStack.getTop().scale(new Vector3(widthLowerArm/2,widthLowerArm/2,lenLowerArm/2));
 			glUniformMatrix4(modelToCameraMatrixUniform,false,modelToCameraStack.getTop().getBuffer());
-			glDrawElements(GL_TRIANGLES,indicies.length,GL_UNSIGNED_SHORT,0);
+			glDrawElements(GL_TRIANGLES,indices.length,GL_UNSIGNED_SHORT,0);
 			modelToCameraStack.popMatrix();
 			
 			drawWrist(modelToCameraStack);
 		
 		private void drawUpperArm(MatrixStack modelToCameraStack) {
 			modelToCameraStack.pushMatrix();
-			modelToCameraStack.rotateX(angleUpperArm);
+			modelToCameraStack.getTop().rotate(angleUpperArm*(float)Math.PI/180,1,0,0);
 			
 			{
 				modelToCameraStack.pushMatrix();
-				modelToCameraStack.translate(new Vector3(0,0,(sizeUpperArm/2)-1));
-				modelToCameraStack.scale(new Vector3(1,1,sizeUpperArm/2));
+				modelToCameraStack.getTop().translate(new Vector3(0,0,(sizeUpperArm/2)-1));
+				modelToCameraStack.getTop().scale(new Vector3(1,1,sizeUpperArm/2));
 				glUniformMatrix4(modelToCameraMatrixUniform,false,modelToCameraStack.getTop().getBuffer());
-				glDrawElements(GL_TRIANGLES, indicies.length, GL_UNSIGNED_SHORT, 0);
+				glDrawElements(GL_TRIANGLES, indices.length, GL_UNSIGNED_SHORT, 0);
 				modelToCameraStack.popMatrix();
 			}
 			

src/main/java/com/ra4king/opengl/arcsynthesis/gl33/chapter7/example1/Example7_1.java

+package com.ra4king.opengl.arcsynthesis.gl33.chapter7.example1;
+
+import static org.lwjgl.opengl.GL11.*;
+import static org.lwjgl.opengl.GL20.*;
+import static org.lwjgl.opengl.GL32.*;
+
+import org.lwjgl.input.Keyboard;
+
+import com.ra4king.opengl.GLProgram;
+import com.ra4king.opengl.util.Mesh;
+import com.ra4king.opengl.util.ShaderProgram;
+import com.ra4king.opengl.util.math.Matrix4;
+import com.ra4king.opengl.util.math.MatrixStack;
+import com.ra4king.opengl.util.math.Vector3;
+
+public class Example7_1 extends GLProgram {
+	public static void main(String[] args) {
+		new Example7_1().run(true);
+	}
+	
+	private final TreeData[] forest = {
+			new TreeData(-45.0f, -40.0f, 2.0f, 3.0f),
+			new TreeData(-42.0f, -35.0f, 2.0f, 3.0f),
+			new TreeData(-39.0f, -29.0f, 2.0f, 4.0f),
+			new TreeData(-44.0f, -26.0f, 3.0f, 3.0f),
+			new TreeData(-40.0f, -22.0f, 2.0f, 4.0f),
+			new TreeData(-36.0f, -15.0f, 3.0f, 3.0f),
+			new TreeData(-41.0f, -11.0f, 2.0f, 3.0f),
+			new TreeData(-37.0f, -6.0f, 3.0f, 3.0f),
+			new TreeData(-45.0f, 0.0f, 2.0f, 3.0f),
+			new TreeData(-39.0f, 4.0f, 3.0f, 4.0f),
+			new TreeData(-36.0f, 8.0f, 2.0f, 3.0f),
+			new TreeData(-44.0f, 13.0f, 3.0f, 3.0f),
+			new TreeData(-42.0f, 17.0f, 2.0f, 3.0f),
+			new TreeData(-38.0f, 23.0f, 3.0f, 4.0f),
+			new TreeData(-41.0f, 27.0f, 2.0f, 3.0f),
+			new TreeData(-39.0f, 32.0f, 3.0f, 3.0f),
+			new TreeData(-44.0f, 37.0f, 3.0f, 4.0f),
+			new TreeData(-36.0f, 42.0f, 2.0f, 3.0f),
+			
+			new TreeData(-32.0f, -45.0f, 2.0f, 3.0f),
+			new TreeData(-30.0f, -42.0f, 2.0f, 4.0f),
+			new TreeData(-34.0f, -38.0f, 3.0f, 5.0f),
+			new TreeData(-33.0f, -35.0f, 3.0f, 4.0f),
+			new TreeData(-29.0f, -28.0f, 2.0f, 3.0f),
+			new TreeData(-26.0f, -25.0f, 3.0f, 5.0f),
+			new TreeData(-35.0f, -21.0f, 3.0f, 4.0f),
+			new TreeData(-31.0f, -17.0f, 3.0f, 3.0f),
+			new TreeData(-28.0f, -12.0f, 2.0f, 4.0f),
+			new TreeData(-29.0f, -7.0f, 3.0f, 3.0f),
+			new TreeData(-26.0f, -1.0f, 2.0f, 4.0f),
+			new TreeData(-32.0f, 6.0f, 2.0f, 3.0f),
+			new TreeData(-30.0f, 10.0f, 3.0f, 5.0f),
+			new TreeData(-33.0f, 14.0f, 2.0f, 4.0f),
+			new TreeData(-35.0f, 19.0f, 3.0f, 4.0f),
+			new TreeData(-28.0f, 22.0f, 2.0f, 3.0f),
+			new TreeData(-33.0f, 26.0f, 3.0f, 3.0f),
+			new TreeData(-29.0f, 31.0f, 3.0f, 4.0f),
+			new TreeData(-32.0f, 38.0f, 2.0f, 3.0f),
+			new TreeData(-27.0f, 41.0f, 3.0f, 4.0f),
+			new TreeData(-31.0f, 45.0f, 2.0f, 4.0f),
+			new TreeData(-28.0f, 48.0f, 3.0f, 5.0f),
+			
+			new TreeData(-25.0f, -48.0f, 2.0f, 3.0f),
+			new TreeData(-20.0f, -42.0f, 3.0f, 4.0f),
+			new TreeData(-22.0f, -39.0f, 2.0f, 3.0f),
+			new TreeData(-19.0f, -34.0f, 2.0f, 3.0f),
+			new TreeData(-23.0f, -30.0f, 3.0f, 4.0f),
+			new TreeData(-24.0f, -24.0f, 2.0f, 3.0f),
+			new TreeData(-16.0f, -21.0f, 2.0f, 3.0f),
+			new TreeData(-17.0f, -17.0f, 3.0f, 3.0f),
+			new TreeData(-25.0f, -13.0f, 2.0f, 4.0f),
+			new TreeData(-23.0f, -8.0f, 2.0f, 3.0f),
+			new TreeData(-17.0f, -2.0f, 3.0f, 3.0f),
+			new TreeData(-16.0f, 1.0f, 2.0f, 3.0f),
+			new TreeData(-19.0f, 4.0f, 3.0f, 3.0f),
+			new TreeData(-22.0f, 8.0f, 2.0f, 4.0f),
+			new TreeData(-21.0f, 14.0f, 2.0f, 3.0f),
+			new TreeData(-16.0f, 19.0f, 2.0f, 3.0f),
+			new TreeData(-23.0f, 24.0f, 3.0f, 3.0f),
+			new TreeData(-18.0f, 28.0f, 2.0f, 4.0f),
+			new TreeData(-24.0f, 31.0f, 2.0f, 3.0f),
+			new TreeData(-20.0f, 36.0f, 2.0f, 3.0f),
+			new TreeData(-22.0f, 41.0f, 3.0f, 3.0f),
+			new TreeData(-21.0f, 45.0f, 2.0f, 3.0f),
+			
+			new TreeData(-12.0f, -40.0f, 2.0f, 4.0f),
+			new TreeData(-11.0f, -35.0f, 3.0f, 3.0f),
+			new TreeData(-10.0f, -29.0f, 1.0f, 3.0f),
+			new TreeData(-9.0f, -26.0f, 2.0f, 2.0f),
+			new TreeData(-6.0f, -22.0f, 2.0f, 3.0f),
+			new TreeData(-15.0f, -15.0f, 1.0f, 3.0f),
+			new TreeData(-8.0f, -11.0f, 2.0f, 3.0f),
+			new TreeData(-14.0f, -6.0f, 2.0f, 4.0f),
+			new TreeData(-12.0f, 0.0f, 2.0f, 3.0f),
+			new TreeData(-7.0f, 4.0f, 2.0f, 2.0f),
+			new TreeData(-13.0f, 8.0f, 2.0f, 2.0f),
+			new TreeData(-9.0f, 13.0f, 1.0f, 3.0f),
+			new TreeData(-13.0f, 17.0f, 3.0f, 4.0f),
+			new TreeData(-6.0f, 23.0f, 2.0f, 3.0f),
+			new TreeData(-12.0f, 27.0f, 1.0f, 2.0f),
+			new TreeData(-8.0f, 32.0f, 2.0f, 3.0f),
+			new TreeData(-10.0f, 37.0f, 3.0f, 3.0f),
+			new TreeData(-11.0f, 42.0f, 2.0f, 2.0f),
+			
+			new TreeData(15.0f, 5.0f, 2.0f, 3.0f),
+			new TreeData(15.0f, 10.0f, 2.0f, 3.0f),
+			new TreeData(15.0f, 15.0f, 2.0f, 3.0f),
+			new TreeData(15.0f, 20.0f, 2.0f, 3.0f),
+			new TreeData(15.0f, 25.0f, 2.0f, 3.0f),
+			new TreeData(15.0f, 30.0f, 2.0f, 3.0f),
+			new TreeData(15.0f, 35.0f, 2.0f, 3.0f),
+			new TreeData(15.0f, 40.0f, 2.0f, 3.0f),
+			new TreeData(15.0f, 45.0f, 2.0f, 3.0f),
+			
+			new TreeData(25.0f, 5.0f, 2.0f, 3.0f),
+			new TreeData(25.0f, 10.0f, 2.0f, 3.0f),
+			new TreeData(25.0f, 15.0f, 2.0f, 3.0f),
+			new TreeData(25.0f, 20.0f, 2.0f, 3.0f),
+			new TreeData(25.0f, 25.0f, 2.0f, 3.0f),
+			new TreeData(25.0f, 30.0f, 2.0f, 3.0f),
+			new TreeData(25.0f, 35.0f, 2.0f, 3.0f),
+			new TreeData(25.0f, 40.0f, 2.0f, 3.0f),
+			new TreeData(25.0f, 45.0f, 2.0f, 3.0f),
+	};
+	
+	private ProgramData uniformColor, objectColor, uniformColorTint;
+	private Matrix4 perspectiveMatrix;
+	
+	private final Matrix4 identity = new Matrix4().clearToIdentity();
+	
+	private Mesh coneMesh, cylinderMesh, cubeTintMesh, cubeColorMesh, planeMesh;
+	
+	private Vector3 camTarget = new Vector3(0,0.4f,0);
+	private Vector3 sphereCamRelPos = new Vector3(67.5f,-46,150);
+	
+	private boolean drawLookAtPoint = true;
+	
+	public Example7_1() {
+		super("Example 7.1", 500, 500, false);
+	}
+	
+	@Override
+	public void init() {
+		glClearColor(0,0,0,0);
+		glClearDepth(1);
+		
+		uniformColor = loadProgram("example7.1.PosOnly.vert","example7.1.ColorUniform.frag");
+		objectColor = loadProgram("example7.1.PosColor.vert","example7.1.ColorPassthrough.frag");
+		uniformColorTint = loadProgram("example7.1.PosColor.vert","example7.1.ColorMultUniform.frag");
+		
+		perspectiveMatrix = new Matrix4();
+		
+		try {
+			coneMesh = new Mesh(getClass().getResourceAsStream("example7.1.UnitConeTint.xml"));
+			cylinderMesh = new Mesh(getClass().getResourceAsStream("example7.1.UnitCylinderTint.xml"));
+			cubeTintMesh = new Mesh(getClass().getResourceAsStream("example7.1.UnitCubeTint.xml"));
+			cubeColorMesh = new Mesh(getClass().getResourceAsStream("example7.1.UnitCubeColor.xml"));
+			planeMesh = new Mesh(getClass().getResourceAsStream("example7.1.UnitPlane.xml"));
+		}
+		catch(Exception exc) {
+			exc.printStackTrace();
+			destroy();
+		}
+		
+		glEnable(GL_CULL_FACE);
+		glCullFace(GL_BACK);
+		glFrontFace(GL_CW);
+		
+		glEnable(GL_DEPTH_TEST);
+		glDepthMask(true);
+		glDepthFunc(GL_LEQUAL);
+		glDepthRange(0,1);
+		glEnable(GL_DEPTH_CLAMP);
+	}
+	
+	private ProgramData loadProgram(String vs, String fs) {
+		ProgramData data = new ProgramData(new ShaderProgram(readFromFile(vs),readFromFile(fs)));
+		data.modelToWorldMatrixUniform = glGetUniformLocation(data.program.getProgram(), "modelToWorldMatrix");
+		data.worldToCameraMatrixUniform = glGetUniformLocation(data.program.getProgram(), "worldToCameraMatrix");
+		data.cameraToClipMatrixUniform = glGetUniformLocation(data.program.getProgram(), "cameraToClipMatrix");
+		data.baseColorUniform = glGetUniformLocation(data.program.getProgram(), "baseColor");
+		return data;
+	}
+	
+	@Override
+	public void resized() {
+		super.resized();
+		
+		perspectiveMatrix.clearToPerspective(45*(float)Math.PI/180, getWidth(), getHeight(), 1, 1000);
+		
+		uniformColor.program.begin();
+		glUniformMatrix4(uniformColor.cameraToClipMatrixUniform,false,perspectiveMatrix.getBuffer());
+		objectColor.program.begin();
+		glUniformMatrix4(objectColor.cameraToClipMatrixUniform,false,perspectiveMatrix.getBuffer());
+		uniformColorTint.program.begin();
+		glUniformMatrix4(uniformColorTint.cameraToClipMatrixUniform,false,perspectiveMatrix.getBuffer());
+		uniformColorTint.program.end();
+	}
+	
+	@Override
+	public void update(long deltaTime) {
+		float delta = deltaTime / (float)1e9;
+		
+		float speed1 = (Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) ? 4f : 16) * delta;
+		float speed2 = (Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) ? 11.25f : 45f) * delta;
+		float speed3 = (Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) ? 5f : 20) * delta;
+		
+		if(Keyboard.isKeyDown(Keyboard.KEY_W))
+			camTarget.sub(0,0,speed1);
+		if(Keyboard.isKeyDown(Keyboard.KEY_S))
+			camTarget.add(0,0,speed1);
+		
+		if(Keyboard.isKeyDown(Keyboard.KEY_D))
+			camTarget.add(speed1,0,0);
+		if(Keyboard.isKeyDown(Keyboard.KEY_A))
+			camTarget.sub(speed1,0,0);
+		
+		if(Keyboard.isKeyDown(Keyboard.KEY_E))
+			camTarget.sub(speed1,0,0);
+		if(Keyboard.isKeyDown(Keyboard.KEY_Q))
+			camTarget.add(speed1,0,0);
+		
+		if(Keyboard.isKeyDown(Keyboard.KEY_I))
+			sphereCamRelPos.sub(0,speed2,0);
+		if(Keyboard.isKeyDown(Keyboard.KEY_K))
+			sphereCamRelPos.add(0,speed2,0);
+		
+		if(Keyboard.isKeyDown(Keyboard.KEY_J))
+			sphereCamRelPos.sub(speed2,0,0);
+		if(Keyboard.isKeyDown(Keyboard.KEY_L))
+			sphereCamRelPos.add(speed2,0,0);
+		
+		if(Keyboard.isKeyDown(Keyboard.KEY_O))
+			sphereCamRelPos.sub(0,0,speed3);
+		if(Keyboard.isKeyDown(Keyboard.KEY_U))
+			sphereCamRelPos.add(0,0,speed3);
+		
+		sphereCamRelPos.y(clamp(sphereCamRelPos.y(), -78.75f, -1));
+		camTarget.y(camTarget.y() > 0 ? camTarget.y() : 0);
+		sphereCamRelPos.z(sphereCamRelPos.z() > 5 ? sphereCamRelPos.z() : 5);
+	}
+	
+	private float clamp(float value, float low, float high) {
+		return Math.min(Math.max(value, low), high);
+	}
+	
+	@Override
+	public void keyPressed(int key, char c, long nanos) {
+		switch(key) {
+			case Keyboard.KEY_SPACE:
+				drawLookAtPoint = !drawLookAtPoint;
+				System.out.printf("Target: %f, %f, %f\n", camTarget.x(), camTarget.y(), camTarget.z());
+				System.out.printf("Position: %f,  %f, %f\n", sphereCamRelPos.x(), sphereCamRelPos.y(), sphereCamRelPos.z());
+				break;
+		}
+	}
+	
+	private Matrix4 calcLookAtMatrix(Vector3 cameraPoint, Vector3 lookPoint, Vector3 upPoint) {
+		Vector3 lookDir = new Vector3(lookPoint).sub(cameraPoint).normalize();
+		Vector3 upDir = new Vector3(upPoint).normalize();
+		
+		Vector3 rightDir = lookDir.cross(upDir).normalize();
+		Vector3 perpUpDir = rightDir.cross(lookDir);
+		
+		Matrix4 rotMat = new Matrix4().clearToIdentity();
+		rotMat.put(0,rightDir,0);
+		rotMat.put(1,perpUpDir,0);
+		rotMat.put(2,lookDir.mul(-1),0);
+		
+		return rotMat.transpose().translate(cameraPoint.mul(-1));
+	}
+	
+	private Vector3 resolveCamPosition() {
+		double phi = sphereCamRelPos.x() * Math.PI / 180;
+		double theta = (sphereCamRelPos.y() + 90) * Math.PI / 180;
+		
+		float sinTheta = (float)Math.sin(theta);
+		float cosTheta = (float)Math.cos(theta);
+		float cosPhi = (float)Math.cos(phi);
+		float sinPhi = (float)Math.sin(phi);
+		
+		return new Vector3(sinTheta * cosPhi, cosTheta, sinTheta * sinPhi).mul(sphereCamRelPos.z()).add(camTarget);
+	}
+	
+	@Override
+	public void render() {
+		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+		
+		Vector3 camPos = resolveCamPosition();
+		
+		MatrixStack camMatrix = new MatrixStack();
+		camMatrix.getTop().put(calcLookAtMatrix(camPos, camTarget, new Vector3(0,1,0)));
+		
+		uniformColor.program.begin();
+		glUniformMatrix4(uniformColor.worldToCameraMatrixUniform,false,camMatrix.getTop().getBuffer());
+		objectColor.program.begin();
+		glUniformMatrix4(objectColor.worldToCameraMatrixUniform,false,camMatrix.getTop().getBuffer());
+		uniformColorTint.program.begin();
+		glUniformMatrix4(uniformColorTint.worldToCameraMatrixUniform,false,camMatrix.getTop().getBuffer());
+		uniformColorTint.program.end();
+		
+		MatrixStack modelMatrix = new MatrixStack();
+		
+		{
+			modelMatrix.pushMatrix();
+			
+			modelMatrix.getTop().scale(100, 1, 100);
+			
+			uniformColor.program.begin();
+			glUniformMatrix4(uniformColor.modelToWorldMatrixUniform,false,modelMatrix.getTop().getBuffer());
+			glUniform4f(uniformColor.baseColorUniform,0.302f,0.416f,0.0589f,1.0f);
+			planeMesh.render();
+			uniformColor.program.end();
+			
+			modelMatrix.popMatrix();
+		}
+		
+		drawForest(modelMatrix);
+		
+		{
+			modelMatrix.pushMatrix();
+			
+			modelMatrix.getTop().translate(20,0,-10);
+			
+			drawParthenon(modelMatrix);
+			
+			modelMatrix.popMatrix();
+		}
+		
+		if(drawLookAtPoint) {
+			glDisable(GL_DEPTH_TEST);
+			
+			modelMatrix.pushMatrix();
+			
+			modelMatrix.getTop().translate(0, 0, -new Vector3(camTarget).sub(camPos).length()).scale(1,1,1);
+			
+			objectColor.program.begin();
+			glUniformMatrix4(objectColor.modelToWorldMatrixUniform, false, modelMatrix.getTop().getBuffer());
+			glUniformMatrix4(objectColor.worldToCameraMatrixUniform, false, identity.getBuffer());
+			cubeColorMesh.render();
+			objectColor.program.end();
+			
+			modelMatrix.popMatrix();
+			
+			glEnable(GL_DEPTH_TEST);
+		}
+	}
+	
+	private void drawForest(MatrixStack modelMatrix) {
+		for(TreeData tree : forest) {
+			modelMatrix.pushMatrix();
+			modelMatrix.getTop().translate(tree.x,0,tree.z);
+			drawTree(modelMatrix, tree.trunkHeight, tree.coneHeight);
+			modelMatrix.popMatrix();
+		}
+	}
+	
+	private void drawTree(MatrixStack modelMatrix, float trunkHeight, float coneHeight) {
+		modelMatrix.pushMatrix();
+		
+		modelMatrix.getTop().scale(1,trunkHeight,1).translate(0,0.5f,0);
+		
+		uniformColorTint.program.begin();
+		glUniformMatrix4(uniformColorTint.modelToWorldMatrixUniform, false, modelMatrix.getTop().getBuffer());
+		glUniform4f(uniformColorTint.baseColorUniform, 0.694f, 0.4f, 0.106f, 1);
+		cylinderMesh.render();
+		uniformColorTint.program.end();
+		
+		modelMatrix.popMatrix();
+		
+		
+		modelMatrix.pushMatrix();
+		
+		modelMatrix.getTop().translate(0,trunkHeight,0);
+		
+		uniformColorTint.program.begin();
+		glUniformMatrix4(uniformColorTint.modelToWorldMatrixUniform, false, modelMatrix.getTop().getBuffer());
+		glUniform4f(uniformColorTint.baseColorUniform, 0, 1, 0, 1);
+		coneMesh.render();
+		uniformColorTint.program.end();
+		
+		modelMatrix.popMatrix();
+	}
+	
+	private final float parthenonWidth = 14;
+	private final float parthenonLength = 20;
+	private final float parthenonColumnHeight = 5;
+	private final float parthenonBaseHeight = 1;
+	private final float parthenonTopHeight = 2;
+	
+	private final float frontZVal = parthenonLength/2 - 1;
+	private final float rightXVal = parthenonWidth/2 - 1;
+	
+	private void drawParthenon(MatrixStack modelMatrix) {
+		{
+			modelMatrix.pushMatrix();
+			
+			modelMatrix.getTop().scale(parthenonWidth,parthenonBaseHeight,parthenonLength).translate(0,0.5f,0);
+			
+			uniformColorTint.program.begin();
+			glUniformMatrix4(uniformColorTint.modelToWorldMatrixUniform, false, modelMatrix.getTop().getBuffer());
+			glUniform4f(uniformColorTint.baseColorUniform, 0.9f, 0.9f, 0.9f, 0.9f);
+			cubeTintMesh.render();
+			uniformColorTint.program.end();
+			
+			modelMatrix.popMatrix();
+		}
+		
+		{
+			modelMatrix.pushMatrix();
+			
+			modelMatrix.getTop().translate(0,parthenonColumnHeight+parthenonBaseHeight,0)
+								.scale(parthenonWidth,parthenonTopHeight,parthenonLength)
+								.translate(0,0.5f,0);
+			
+			uniformColorTint.program.begin();
+			glUniformMatrix4(uniformColorTint.modelToWorldMatrixUniform, false, modelMatrix.getTop().getBuffer());
+			glUniform4f(uniformColorTint.baseColorUniform, 0.9f, 0.9f, 0.9f, 0.9f);
+			cubeTintMesh.render();
+			uniformColorTint.program.end();
+			
+			modelMatrix.popMatrix();
+		}
+		
+		for(int a = 0; a < parthenonWidth/2; a++) {
+			{
+				modelMatrix.pushMatrix();
+				modelMatrix.getTop().translate(2*a - parthenonWidth/2 + 1, parthenonBaseHeight, frontZVal);
+				drawColumn(modelMatrix);
+				modelMatrix.popMatrix();
+			}
+			
+			{
+				modelMatrix.pushMatrix();
+				modelMatrix.getTop().translate(2*a - parthenonWidth/2 + 1, parthenonBaseHeight, -frontZVal);
+				drawColumn(modelMatrix);
+				modelMatrix.popMatrix();
+			}
+		}
+		
+		for(int a = 1; a < (parthenonLength-2)/2; a++) {
+			{
+				modelMatrix.pushMatrix();
+				modelMatrix.getTop().translate(rightXVal,parthenonBaseHeight,2*a - parthenonLength/2 + 1);
+				drawColumn(modelMatrix);
+				modelMatrix.popMatrix();
+			}
+			
+			{
+				modelMatrix.pushMatrix();
+				modelMatrix.getTop().translate(-rightXVal,parthenonBaseHeight,2*a - parthenonLength/2 + 1);
+				drawColumn(modelMatrix);
+				modelMatrix.popMatrix();
+			}
+		}
+		
+		{
+			modelMatrix.pushMatrix();
+			
+			modelMatrix.getTop().translate(0,1,0).scale(parthenonWidth-6, parthenonColumnHeight, parthenonLength-6).translate(0,0.5f,0);
+			
+			objectColor.program.begin();
+			glUniformMatrix4(objectColor.modelToWorldMatrixUniform, false, modelMatrix.getTop().getBuffer());
+			cubeColorMesh.render();
+			objectColor.program.end();
+			
+			modelMatrix.popMatrix();
+		}
+		
+		{
+			modelMatrix.pushMatrix();
+			
+			modelMatrix.getTop().translate(0,parthenonColumnHeight + parthenonBaseHeight + parthenonTopHeight/2,parthenonLength/2)
+								.rotate(-135*(float)Math.PI/180,1,0,0)
+								.rotate(45*(float)Math.PI/180, 0, 1, 0);
+			
+			objectColor.program.begin();
+			glUniformMatrix4(objectColor.modelToWorldMatrixUniform, false, modelMatrix.getTop().getBuffer());
+			cubeColorMesh.render();
+			objectColor.program.end();
+
+			modelMatrix.popMatrix();
+		}
+	}
+	
+	private final float columnBaseHeight = 0.25f;
+	
+	private void drawColumn(MatrixStack modelMatrix) {
+		{
+			modelMatrix.pushMatrix();
+			
+			modelMatrix.getTop().scale(1, columnBaseHeight, 1).translate(0,0.5f,0);
+			
+			uniformColorTint.program.begin();
+			glUniformMatrix4(uniformColorTint.modelToWorldMatrixUniform, false, modelMatrix.getTop().getBuffer());
+			glUniform4f(uniformColorTint.baseColorUniform, 1, 1, 1, 1);
+			cubeTintMesh.render();
+			uniformColorTint.program.end();
+
+			modelMatrix.popMatrix();
+		}
+		
+		{
+			modelMatrix.pushMatrix();
+			
+			modelMatrix.getTop().translate(0, parthenonColumnHeight, 0).scale(1,columnBaseHeight,1).translate(0,0.5f,0);
+			
+			uniformColorTint.program.begin();
+			glUniformMatrix4(uniformColorTint.modelToWorldMatrixUniform, false, modelMatrix.getTop().getBuffer());
+			glUniform4f(uniformColorTint.baseColorUniform, 0.9f, 0.9f, 0.9f, 0.9f);
+			cubeTintMesh.render();
+			uniformColorTint.program.end();
+
+			modelMatrix.popMatrix();
+		}
+		
+		{
+			modelMatrix.pushMatrix();
+			
+			modelMatrix.getTop().translate(0, columnBaseHeight, 0).scale(0.8f,  parthenonColumnHeight - columnBaseHeight*2,  0.8f).translate(0,0.5f,0);
+			
+			uniformColorTint.program.begin();
+			glUniformMatrix4(uniformColorTint.modelToWorldMatrixUniform, false, modelMatrix.getTop().getBuffer());
+			glUniform4f(uniformColorTint.baseColorUniform, 0.9f, 0.9f, 0.9f, 0.9f);
+			cylinderMesh.render();
+			uniformColorTint.program.end();
+
+			modelMatrix.popMatrix();
+		}
+	}
+	
+	private static class ProgramData {
+		private ShaderProgram program;
+		private int modelToWorldMatrixUniform;
+		private int worldToCameraMatrixUniform;
+		private int cameraToClipMatrixUniform;
+		private int baseColorUniform;
+		
+		public ProgramData(ShaderProgram program) {
+			this.program = program;
+		}
+	}
+	
+	private static class TreeData {
+		private float x, z, trunkHeight, coneHeight;
+		
+		public TreeData(float x, float z, float trunkHeight, float coneHeight) {
+			this.x = x;
+			this.z = z;
+			this.trunkHeight = trunkHeight;
+			this.coneHeight = coneHeight;
+		}
+	}
+}

src/main/java/com/ra4king/opengl/arcsynthesis/gl33/chapter7/example1/example7.1.ColorMultUniform.frag

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

src/main/java/com/ra4king/opengl/arcsynthesis/gl33/chapter7/example1/example7.1.ColorPassthrough.frag

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

src/main/java/com/ra4king/opengl/arcsynthesis/gl33/chapter7/example1/example7.1.ColorUniform.frag

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

src/main/java/com/ra4king/opengl/arcsynthesis/gl33/chapter7/example1/example7.1.PosColor.vert

+#version 330
+
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec4 color;
+
+smooth out vec4 interpColor;
+
+uniform mat4 cameraToClipMatrix;
+uniform mat4 worldToCameraMatrix;
+uniform mat4 modelToWorldMatrix;
+
+void main()
+{
+	gl_Position = cameraToClipMatrix * worldToCameraMatrix * modelToWorldMatrix * position;
+	interpColor = color;
+}

src/main/java/com/ra4king/opengl/arcsynthesis/gl33/chapter7/example1/example7.1.PosOnly.vert

+#version 330
+
+layout(location = 0) in vec4 position;
+
+uniform mat4 cameraToClipMatrix;
+uniform mat4 worldToCameraMatrix;
+uniform mat4 modelToWorldMatrix;
+
+void main()
+{
+	gl_Position = cameraToClipMatrix * worldToCameraMatrix * modelToWorldMatrix * position;
+}

src/main/java/com/ra4king/opengl/arcsynthesis/gl33/chapter7/example1/example7.1.UnitConeTint.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<?oxygen RNGSchema="../../Documents/meshFormat.rnc" type="compact"?>
+
+<mesh xmlns="http://www.arcsynthesis.com/gltut/mesh" >
+	<attribute index="0" type="float" size="3" > 
+		0 0.866 0
+		0.5 0 0
+		0.48907381875731 0 0.1039557588888
+		0.45677280077542 0 0.20336815992623
+		0.40450865316151 0 0.29389241146627
+		0.33456556611288 0 0.37157217599218
+		0.2500003830126 0 0.43301248075957
+		0.15450900193016 0 0.47552809414644
+		0.052264847412855 0 0.49726088296277
+		-0.052263527886268 0 0.49726102165048
+		-0.15450774007312 0 0.47552850414828
+		-0.24999923397422 0 0.43301314415651
+		-0.33456458011157 0 0.37157306379065
+		-0.40450787329018 0 0.29389348486527
+		-0.45677226111814 0 0.20336937201315
+		-0.48907354289964 0 0.10395705668972
+		-0.49999999999824 0 1.3267948966764e-006
+		-0.48907409461153 0 -0.10395446108714
+		-0.45677334042948 0 -0.20336694783787
+		-0.40450943302999 0 -0.2938913380652
+		-0.33456655211184 0 -0.3715712881911
+		-0.25000153204922 0 -0.43301181735958
+		-0.15451026378611 0 -0.47552768414126
+		-0.052266166939075 0 -0.49726074427155
+		0.052262208359312 0 -0.4972611603347
+		0.15450647821499 0 -0.47552891414676
+		0.24999808493408 0 -0.4330138075504
+		0.3345635941079 0 -0.37157395158649
+		0.40450709341601 0 -0.2938945582622
+		0.45677172145764 0 -0.20337058409865
+		0.48907326703854 0 -0.10395835448992
+		0 0 0
+	</attribute>
+	<attribute index="1" type="float" size="4" > 
+		1 1 1 1
+		0.9 0.9 0.9 1
+		0.82 0.82 0.82 1
+		0.74 0.74 0.74 1
+		0.66 0.66 0.66 1
+		0.58 0.58 0.58 1
+		0.5 0.5 0.5 1
+		0.58 0.58 0.58 1
+		0.66 0.66 0.66 1
+		0.74 0.74 0.74 1
+		0.82 0.82 0.82 1
+		0.9 0.9 0.9 1
+		0.82 0.82 0.82 1
+		0.74 0.74 0.74 1
+		0.66 0.66 0.66 1
+		0.58 0.58 0.58 1
+		0.5 0.5 0.5 1
+		0.58 0.58 0.58 1
+		0.66 0.66 0.66 1
+		0.74 0.74 0.74 1
+		0.82 0.82 0.82 1
+		0.9 0.9 0.9 1
+		0.82 0.82 0.82 1
+		0.74 0.74 0.74 1
+		0.66 0.66 0.66 1
+		0.58 0.58 0.58 1
+		0.5 0.5 0.5 1
+		0.58 0.58 0.58 1
+		0.66 0.66 0.66 1
+		0.74 0.74 0.74 1
+		0.82 0.82 0.82 1
+		0.9 0.9 0.9 1
+	</attribute>
+	<indices cmd="tri-fan" type="ushort" >0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 1</indices>
+	<indices cmd="tri-fan" type="ushort" >31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 30</indices>
+</mesh>

src/main/java/com/ra4king/opengl/arcsynthesis/gl33/chapter7/example1/example7.1.UnitCubeColor.xml

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

src/main/java/com/ra4king/opengl/arcsynthesis/gl33/chapter7/example1/example7.1.UnitCubeTint.xml

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

src/main/java/com/ra4king/opengl/arcsynthesis/gl33/chapter7/example1/example7.1.UnitCylinderTint.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<?oxygen RNGSchema="../../Documents/meshFormat.rnc" type="compact"?>
+
+<mesh xmlns="http://www.arcsynthesis.com/gltut/mesh" >
+	<attribute index="0" type="float" size="3" > 
+		0 0.5 0
+		0.5 0.5 0
+		0.5 -0.5 0
+		0.48907381875731 0.5 0.1039557588888
+		0.48907381875731 -0.5 0.1039557588888
+		0.45677280077542 0.5 0.20336815992623
+		0.45677280077542 -0.5 0.20336815992623
+		0.40450865316151 0.5 0.29389241146627
+		0.40450865316151 -0.5 0.29389241146627
+		0.33456556611288 0.5 0.37157217599218
+		0.33456556611288 -0.5 0.37157217599218
+		0.2500003830126 0.5 0.43301248075957
+		0.2500003830126 -0.5 0.43301248075957
+		0.15450900193016 0.5 0.47552809414644
+		0.15450900193016 -0.5 0.47552809414644
+		0.052264847412855 0.5 0.49726088296277
+		0.052264847412855 -0.5 0.49726088296277
+		-0.052263527886268 0.5 0.49726102165048
+		-0.052263527886268 -0.5 0.49726102165048
+		-0.15450774007312 0.5 0.47552850414828
+		-0.15450774007312 -0.5 0.47552850414828
+		-0.24999923397422 0.5 0.43301314415651
+		-0.24999923397422 -0.5 0.43301314415651
+		-0.33456458011157 0.5 0.37157306379065
+		-0.33456458011157 -0.5 0.37157306379065
+		-0.40450787329018 0.5 0.29389348486527
+		-0.40450787329018 -0.5 0.29389348486527
+		-0.45677226111814 0.5 0.20336937201315
+		-0.45677226111814 -0.5 0.20336937201315
+		-0.48907354289964 0.5 0.10395705668972
+		-0.48907354289964 -0.5 0.10395705668972
+		-0.49999999999824 0.5 1.3267948966764e-006
+		-0.49999999999824 -0.5 1.3267948966764e-006
+		-0.48907409461153 0.5 -0.10395446108714
+		-0.48907409461153 -0.5 -0.10395446108714
+		-0.45677334042948 0.5 -0.20336694783787
+		-0.45677334042948 -0.5 -0.20336694783787
+		-0.40450943302999 0.5 -0.2938913380652
+		-0.40450943302999 -0.5 -0.2938913380652
+		-0.33456655211184 0.5 -0.3715712881911
+		-0.33456655211184 -0.5 -0.3715712881911
+		-0.25000153204922 0.5 -0.43301181735958
+		-0.25000153204922 -0.5 -0.43301181735958
+		-0.15451026378611 0.5 -0.47552768414126
+		-0.15451026378611 -0.5 -0.47552768414126
+		-0.052266166939075 0.5 -0.49726074427155
+		-0.052266166939075 -0.5 -0.49726074427155
+		0.052262208359312 0.5 -0.4972611603347
+		0.052262208359312 -0.5 -0.4972611603347
+		0.15450647821499 0.5 -0.47552891414676
+		0.15450647821499 -0.5 -0.47552891414676
+		0.24999808493408 0.5 -0.4330138075504
+		0.24999808493408 -0.5 -0.4330138075504
+		0.3345635941079 0.5 -0.37157395158649
+		0.3345635941079 -0.5 -0.37157395158649
+		0.40450709341601 0.5 -0.2938945582622
+		0.40450709341601 -0.5 -0.2938945582622
+		0.45677172145764 0.5 -0.20337058409865
+		0.45677172145764 -0.5 -0.20337058409865
+		0.48907326703854 0.5 -0.10395835448992
+		0.48907326703854 -0.5 -0.10395835448992
+		0 -0.5 0</attribute>
+	<attribute index="1" type="float" size="4" > 
+		1 1 1 1
+		0.9 0.9 0.9 1
+		0.9 0.9 0.9 1
+		0.82 0.82 0.82 1
+		0.82 0.82 0.82 1
+		0.74 0.74 0.74 1
+		0.74 0.74 0.74 1
+		0.66 0.66 0.66 1
+		0.66 0.66 0.66 1
+		0.58 0.58 0.58 1
+		0.58 0.58 0.58 1
+		0.5 0.5 0.5 1
+		0.5 0.5 0.5 1
+		0.58 0.58 0.58 1
+		0.58 0.58 0.58 1
+		0.66 0.66 0.66 1
+		0.66 0.66 0.66 1
+		0.74 0.74 0.74 1
+		0.74 0.74 0.74 1
+		0.82 0.82 0.82 1
+		0.82 0.82 0.82 1
+		0.9 0.9 0.9 1
+		0.9 0.9 0.9 1
+		0.82 0.82 0.82 1
+		0.82 0.82 0.82 1
+		0.74 0.74 0.74 1
+		0.74 0.74 0.74 1
+		0.66 0.66 0.66 1
+		0.66 0.66 0.66 1
+		0.58 0.58 0.58 1
+		0.58 0.58 0.58 1
+		0.5 0.5 0.5 1
+		0.5 0.5 0.5 1
+		0.58 0.58 0.58 1
+		0.58 0.58 0.58 1
+		0.66 0.66 0.66 1
+		0.66 0.66 0.66 1
+		0.74 0.74 0.74 1
+		0.74 0.74 0.74 1
+		0.82 0.82 0.82 1
+		0.82 0.82 0.82 1
+		0.9 0.9 0.9 1
+		0.9 0.9 0.9 1
+		0.82 0.82 0.82 1
+		0.82 0.82 0.82 1
+		0.74 0.74 0.74 1
+		0.74 0.74 0.74 1
+		0.66 0.66 0.66 1
+		0.66 0.66 0.66 1
+		0.58 0.58 0.58 1
+		0.58 0.58 0.58 1
+		0.5 0.5 0.5 1
+		0.5 0.5 0.5 1
+		0.58 0.58 0.58 1
+		0.58 0.58 0.58 1
+		0.66 0.66 0.66 1
+		0.66 0.66 0.66 1
+		0.74 0.74 0.74 1
+		0.74 0.74 0.74 1
+		0.82 0.82 0.82 1
+		0.82 0.82 0.82 1
+		1 1 1 1
+	</attribute>
+	<indices cmd="tri-fan" type="ushort" >0 1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 41 43 45 47 49 51 53 55 57 59 1</indices>
+	<indices cmd="tri-fan" type="ushort" >61 60 58 56 54 52 50 48 46 44 42 40 38 36 34 32 30 28 26 24 22 20 18 16 14 12 10 8 6 4 2 60</indices>
+	<indices cmd="tri-strip" type="ushort" >1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 1 2</indices>
+</mesh>

src/main/java/com/ra4king/opengl/arcsynthesis/gl33/chapter7/example1/example7.1.UnitPlane.xml

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

src/main/java/com/ra4king/opengl/util/Mesh.java

+package com.ra4king.opengl.util;
+
+import static org.lwjgl.opengl.GL11.*;
+import static org.lwjgl.opengl.GL15.*;
+import static org.lwjgl.opengl.GL20.*;
+import static org.lwjgl.opengl.GL30.*;
+
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.nio.ShortBuffer;
+import java.util.ArrayList;
+
+import org.lwjgl.BufferUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserFactory;
+
+public class Mesh {
+	private int vao;
+	private ArrayList<RenderCmd> renderCommands;
+	
+	public Mesh(InputStream file) throws Exception {
+		renderCommands = new ArrayList<RenderCmd>();
+		
+		XmlPullParser xml = XmlPullParserFactory.newInstance().newPullParser();
+		xml.setInput(file, "UTF-8");
+		
+		xml.next();
+		
+		xml.require(XmlPullParser.START_TAG, null, "mesh");
+		
+		ArrayList<Attribute> attributes = new ArrayList<Attribute>();
+		
+		ByteBuffer attributeData = BufferUtils.createByteBuffer(0), indexData = BufferUtils.createByteBuffer(0);
+		
+		do {
+			xml.nextTag();
+			
+			switch(xml.getName()) {
+				case "attribute": {
+					int index = Integer.parseInt(xml.getAttributeValue(null, "index"));
+					String type = xml.getAttributeValue(null, "type");
+					int size = Integer.parseInt(xml.getAttributeValue(null, "size"));
+					
+					Attribute attrib = new Attribute(index,type,size);
+					attributes.add(attrib);
+					
+					xml.next();
+					xml.require(XmlPullParser.TEXT, null, null);
+					
+					attributeData = attrib.storeData(attributeData, clean(xml.getText().trim().replace("\r\n", " ").replace('\n', ' ').split(" ")));
+					
+					xml.next();
+					xml.require(XmlPullParser.END_TAG, null, "attribute");
+					
+					break;
+				}
+				case "indices": {
+					String primitive = xml.getAttributeValue(null, "cmd");
+					String type = xml.getAttributeValue(null, "type");
+					
+					RenderCmd cmd = new RenderCmd(primitive,type);
+					renderCommands.add(cmd);
+					
+					xml.next();
+					xml.require(XmlPullParser.TEXT, null, null);
+					
+					indexData = cmd.storeData(indexData, clean(xml.getText().trim().replace("\r\n", " ").replace('\n', ' ').split(" ")));
+					
+					xml.next();
+					xml.require(XmlPullParser.END_TAG, null, "indices");
+					
+					break;
+				}
+				case "arrays": {
+					String primitive = xml.getAttributeValue(null, "cmd");
+					int start = Integer.parseInt(xml.getAttributeValue(null, "start"));
+					int count = Integer.parseInt(xml.getAttributeValue(null, "count"));
+					
+					RenderCmd cmd = new RenderCmd(primitive,start,count);
+					renderCommands.add(cmd);
+					
+					xml.next();
+					xml.require(XmlPullParser.END_TAG, null, "arrays");
+					
+					break;
+				}
+				case "mesh":
+					break;
+				default:
+					throw new IllegalArgumentException("Invalid TAG name: " + xml.getName());
+			}
+		} while(xml.next() != XmlPullParser.END_DOCUMENT);
+		
+		if(attributes.size() == 0)
+			throw new IllegalArgumentException("There must be at least 1 set of attributes.");
+		if(renderCommands.size() == 0)
+			throw new IllegalArgumentException("There must be at least 1 render command.");
+		
+		attributeData.flip();
+		indexData.flip();
+		
+		int vbo1 = glGenBuffers();
+		glBindBuffer(GL_ARRAY_BUFFER, vbo1);
+		glBufferData(GL_ARRAY_BUFFER, (ByteBuffer)BufferUtils.createByteBuffer(attributeData.capacity()).put(attributeData).flip(),GL_STATIC_DRAW);
+		glBindBuffer(GL_ARRAY_BUFFER, 0);
+		
+		int vbo2 = -1;
+		if(indexData.hasRemaining()) {
+			vbo2 = glGenBuffers();
+			glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo2);
+			glBufferData(GL_ELEMENT_ARRAY_BUFFER, (ByteBuffer)BufferUtils.createByteBuffer(indexData.capacity()).put(indexData).flip(),GL_STATIC_DRAW);
+			glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+		}
+		
+		vao = glGenVertexArrays();
+		glBindVertexArray(vao);
+		
+		glBindBuffer(GL_ARRAY_BUFFER, vbo1);
+		for(Attribute attrib : attributes) {
+			glEnableVertexAttribArray(attrib.index);
+			glVertexAttribPointer(attrib.index, attrib.size, attrib.type.dataType, attrib.type.normalized, 0, attrib.offset);
+		}
+		
+		if(vbo2 >= 0)
+			glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo2);
+		
+		glBindVertexArray(0);
+		glBindBuffer(GL_ARRAY_BUFFER, 0);
+		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+	}
+	
+	private String[] clean(String[] data) {
+		ArrayList<String> clean = new ArrayList<String>();
+		for(String s : data)
+			if(!(s = s.trim()).isEmpty())
+				clean.add(s);
+		return clean.toArray(new String[0]);
+	}
+	
+	public void render() {
+		glBindVertexArray(vao);
+		for(RenderCmd cmd : renderCommands)
+			cmd.render();
+		glBindVertexArray(0);
+	}
+	
+	private static class RenderCmd {
+		private boolean isIndexedCmd;
+		private int primitive;
+		private int start;
+		private int count;
+		private RenderCommandType type;
+		
+		public RenderCmd(String primitive, String type) {
+			isIndexedCmd = true;
+			this.primitive = getPrimitive(primitive);
+			this.type = RenderCommandType.getRenderType(type);
+		}
+		
+		public RenderCmd(String primitive, int start, int count) {
+			isIndexedCmd = false;
+			this.primitive = getPrimitive(primitive);
+			this.start = start;
+			this.count = count;
+		}
+		
+		public ByteBuffer storeData(ByteBuffer b, String[] data) {
+			start = b.position();
+			count = data.length;
+			
+			ByteBuffer b2 = BufferUtils.createByteBuffer(start+count*type.size);
+			b2.put((ByteBuffer)b.flip());
+			type.store(b2, data);
+			
+			return b2;
+		}
+		
+		private static int getPrimitive(String name) {
+			switch(name) {
+				case "triangles": return GL_TRIANGLES;
+				case "tri-fan": return GL_TRIANGLE_FAN;
+				case "tri-strip": return GL_TRIANGLE_STRIP;
+				case "lines": return GL_LINES;
+				case "line-strip": return GL_LINE_STRIP;
+				case "line-loop": return GL_LINE_LOOP;
+				case "points": return GL_POINTS;
+				default: throw new IllegalArgumentException("Invalid primitive name: " + name);
+			}
+		}
+		
+		public void render() {
+			if(isIndexedCmd)
+				glDrawElements(primitive, count, type.dataType, start);
+			else
+				glDrawArrays(primitive, start, count);
+		}
+	}
+	
+	private enum RenderCommandType {
+		UBYTE("ubyte",GL_UNSIGNED_BYTE,1), USHORT("ushort",GL_UNSIGNED_SHORT,2), UINT("uint",GL_UNSIGNED_INT,4);
+		
+		private String name;
+		private int dataType;
+		private int size;
+		
+		private RenderCommandType(String name, int type, int size) {
+			this.name = name;
+			this.dataType = type;
+			this.size = size;
+		}
+		
+		public void store(ByteBuffer b, String[] data) {
+			switch(dataType) {
+				case GL_UNSIGNED_BYTE:
+					for(String s : data)
+						b.put(Byte.parseByte(s));
+					break;