Browse Source

Automated terrain generation and improved RawModel class.

Frans Bergman 8 years ago
parent
commit
39e452622a
25 changed files with 481 additions and 382 deletions
  1. 1 0
      .gitignore
  2. 4 2
      TODO.txt
  3. 31 30
      src/main/java/eu/tankernn/gameEngine/MainLoop.java
  4. 1 1
      src/main/java/eu/tankernn/gameEngine/entities/PlayerCamera.java
  5. 13 33
      src/main/java/eu/tankernn/gameEngine/loader/Loader.java
  6. 0 26
      src/main/java/eu/tankernn/gameEngine/loader/models/RawModel.java
  7. 1 0
      src/main/java/eu/tankernn/gameEngine/loader/models/TexturedModel.java
  8. 1 1
      src/main/java/eu/tankernn/gameEngine/loader/obj/normalMapped/NormalMappedObjLoader.java
  9. 5 5
      src/main/java/eu/tankernn/gameEngine/particles/ParticleRenderer.java
  10. 3 7
      src/main/java/eu/tankernn/gameEngine/postProcessing/PostProcessing.java
  11. 11 5
      src/main/java/eu/tankernn/gameEngine/renderEngine/RawModel.java
  12. 5 13
      src/main/java/eu/tankernn/gameEngine/renderEngine/entities/EntityRenderer.java
  13. 4 8
      src/main/java/eu/tankernn/gameEngine/renderEngine/gui/GuiRenderer.java
  14. 12 21
      src/main/java/eu/tankernn/gameEngine/renderEngine/normalMap/NormalMappingRenderer.java
  15. 5 8
      src/main/java/eu/tankernn/gameEngine/renderEngine/shadows/ShadowMapEntityRenderer.java
  16. 3 3
      src/main/java/eu/tankernn/gameEngine/renderEngine/skybox/CubeGenerator.java
  17. 3 3
      src/main/java/eu/tankernn/gameEngine/renderEngine/skybox/Skybox.java
  18. 2 2
      src/main/java/eu/tankernn/gameEngine/renderEngine/skybox/SkyboxRenderer.java
  19. 5 13
      src/main/java/eu/tankernn/gameEngine/renderEngine/terrain/TerrainRenderer.java
  20. 3 4
      src/main/java/eu/tankernn/gameEngine/renderEngine/water/WaterRenderer.java
  21. 2 0
      src/main/java/eu/tankernn/gameEngine/settings/Settings.java
  22. 61 174
      src/main/java/eu/tankernn/gameEngine/terrains/Terrain.java
  23. 174 0
      src/main/java/eu/tankernn/gameEngine/terrains/TerrainModelData.java
  24. 118 17
      src/main/java/eu/tankernn/gameEngine/terrains/TerrainPack.java
  25. 13 6
      src/main/java/eu/tankernn/gameEngine/util/InternalFile.java

+ 1 - 0
.gitignore

@@ -1,3 +1,4 @@
 /bin/
 /target/
 /res/
+res

+ 4 - 2
TODO.txt

@@ -1,5 +1,7 @@
-Make text variables uniform/ change text rendering method
-Fix player movement
+Infinite terrain
+Multiplayer
+Modular GUIs
+ - Make text variables uniform/ change text rendering method
 
 Useful regex-expressions:
 

+ 31 - 30
src/main/java/eu/tankernn/gameEngine/MainLoop.java

@@ -21,7 +21,6 @@ import eu.tankernn.gameEngine.environmentMap.EnvironmentMapRenderer;
 import eu.tankernn.gameEngine.font.meshCreator.FontType;
 import eu.tankernn.gameEngine.font.meshCreator.GUIText;
 import eu.tankernn.gameEngine.loader.Loader;
-import eu.tankernn.gameEngine.loader.models.RawModel;
 import eu.tankernn.gameEngine.loader.models.TexturedModel;
 import eu.tankernn.gameEngine.loader.obj.ModelData;
 import eu.tankernn.gameEngine.loader.obj.OBJFileLoader;
@@ -36,6 +35,7 @@ import eu.tankernn.gameEngine.postProcessing.MultisampleMultitargetFbo;
 import eu.tankernn.gameEngine.postProcessing.PostProcessing;
 import eu.tankernn.gameEngine.renderEngine.DisplayManager;
 import eu.tankernn.gameEngine.renderEngine.MasterRenderer;
+import eu.tankernn.gameEngine.renderEngine.RawModel;
 import eu.tankernn.gameEngine.renderEngine.Scene;
 import eu.tankernn.gameEngine.renderEngine.font.TextMaster;
 import eu.tankernn.gameEngine.renderEngine.gui.GuiRenderer;
@@ -43,6 +43,7 @@ import eu.tankernn.gameEngine.renderEngine.gui.GuiTexture;
 import eu.tankernn.gameEngine.renderEngine.skybox.Skybox;
 import eu.tankernn.gameEngine.renderEngine.water.WaterMaster;
 import eu.tankernn.gameEngine.renderEngine.water.WaterTile;
+import eu.tankernn.gameEngine.settings.Settings;
 import eu.tankernn.gameEngine.terrains.Terrain;
 import eu.tankernn.gameEngine.terrains.TerrainPack;
 import eu.tankernn.gameEngine.util.DistanceSorter;
@@ -54,9 +55,9 @@ public class MainLoop {
 	private static final int SEED = 1235;
 
 	// Skybox settings
-	public static final String[] TEXTURE_FILES = { "alps_rt", "alps_lf", "alps_up", "alps_dn", "alps_bk", "alps_ft" };
-	public static final String[] NIGHT_TEXTURE_FILES = { "midnight_rt", "midnight_lf", "midnight_up", "midnight_dn",
-			"midnight_bk", "midnight_ft" };
+	public static final String[] TEXTURE_FILES = { "alps_ft", "alps_bk", "alps_up", "alps_dn", "alps_rt", "alps_lf", };
+	public static final String[] NIGHT_TEXTURE_FILES = { "midnight_ft", "midnight_bk", "midnight_up", "midnight_dn",
+			"midnight_rt", "midnight_lf" };
 
 	// Water settings
 	public static final String DUDV_MAP = "waterDUDV.png";
@@ -64,15 +65,27 @@ public class MainLoop {
 
 	private static final boolean DEBUG = true;
 
-	public static void main(String[] args) throws FileNotFoundException {
-
-		List<Entity> entities = new ArrayList<Entity>();
-		List<Entity> normalMapEntities = new ArrayList<Entity>();
-		TerrainPack terrainPack = new TerrainPack();
+	static List<Entity> entities = new ArrayList<Entity>();
+	static List<Entity> normalMapEntities = new ArrayList<Entity>();
+	static List<Light> lights = new ArrayList<Light>();
 
+	public static void main(String[] args) throws FileNotFoundException {
 		DisplayManager.createDisplay("Tankernn Game Engine tester");
+
 		Loader loader = new Loader();
 
+		// ### Terrain textures ###
+
+		Texture backgroundTexture = loader.loadTexture("grassy.png");
+		Texture rTexture = loader.loadTexture("dirt.png");
+		Texture gTexture = loader.loadTexture("pinkFlowers.png");
+		Texture bTexture = loader.loadTexture("path.png");
+
+		TerrainTexturePack texturePack = new TerrainTexturePack(backgroundTexture, rTexture, gTexture, bTexture);
+		Texture blendMap = loader.loadTexture("blendMap.png");
+
+		TerrainPack terrainPack = new TerrainPack(loader, texturePack, blendMap, SEED);
+
 		// Player
 		ModelData playrModelData = OBJFileLoader.loadOBJ(new InternalFile("dragon.obj"));
 		RawModel playerModel = loader.loadToVAO(playrModelData);
@@ -110,18 +123,16 @@ public class MainLoop {
 		// Barrel
 		TexturedModel barrelModel = new TexturedModel(loader.loadOBJ(new InternalFile("barrel.obj")),
 				new ModelTexture(loader.loadTexture("barrel.png")));
-		
+
 		barrelModel.getModelTexture().setNormalMap(loader.loadTexture("barrelNormal.png"))
-									.setSpecularMap(loader.loadTexture("barrelS.png"))
-									.setShineDamper(10)
-									.setReflectivity(0.5f);
-		
+				.setSpecularMap(loader.loadTexture("barrelS.png")).setShineDamper(10).setReflectivity(0.5f);
+
 		Entity barrel = new Entity(barrelModel, new Vector3f(75, 10, 75), 0, 0, 0, 1f);
 		normalMapEntities.add(barrel);
 
 		Light sun = new Light(new Vector3f(100000, 150000, -70000), new Vector3f(1f, 1f, 1f));
 		Light flashLight = new Light(new Vector3f(0, 10, -10), new Vector3f(2, 0, 0), new Vector3f(1, 0.01f, 0.002f));
-		List<Light> lights = new ArrayList<Light>();
+
 		lights.add(sun);
 		lights.add(flashLight);
 		lights.add(new Light(new Vector3f(10, 100, 0), new Vector3f(0, 1, 0)));
@@ -129,20 +140,7 @@ public class MainLoop {
 		lights.add(new Light(new Vector3f(30, 100, 0), new Vector3f(1, 0, 0)));
 		lights.add(new Light(new Vector3f(40, 100, 0), new Vector3f(1, 1, 0)));
 
-		// ### Terrain textures ###
-
-		Texture backgroundTexture = loader.loadTexture("grassy.png");
-		Texture rTexture = loader.loadTexture("dirt.png");
-		Texture gTexture = loader.loadTexture("pinkFlowers.png");
-		Texture bTexture = loader.loadTexture("path.png");
-
-		TerrainTexturePack texturePack = new TerrainTexturePack(backgroundTexture, rTexture, gTexture, bTexture);
-		Texture blendMap = loader.loadTexture("blendMap.png");
-
-		terrainPack.addTerrain(new Terrain(0, 1, loader, texturePack, blendMap, SEED));
-		terrainPack.addTerrain(new Terrain(1, 1, loader, texturePack, blendMap, SEED));
-		terrainPack.addTerrain(new Terrain(0, 0, loader, texturePack, blendMap, SEED));
-		terrainPack.addTerrain(new Terrain(1, 0, loader, texturePack, blendMap, SEED));
+		terrainPack.update(player);
 
 		// ### Random grass generation ###
 
@@ -190,6 +188,7 @@ public class MainLoop {
 		while (!Display.isCloseRequested()) {
 			barrel.increaseRotation(0, 1, 0);
 			player.move(terrainPack);
+			terrainPack.update(player);
 			camera.update();
 			picker.update();
 
@@ -216,7 +215,8 @@ public class MainLoop {
 					text.remove();
 					Vector3f pos = player.getPosition();
 					String textString = "X: " + Math.floor(pos.x) + " Y: " + Math.floor(pos.y) + " Z: "
-							+ Math.floor(pos.z);
+							+ Math.floor(pos.z) + " Current terrain: " + currentTerrain.getX() / Settings.TERRAIN_SIZE
+							+ ":" + currentTerrain.getZ() / Settings.TERRAIN_SIZE;
 					text = new GUIText(textString, 1, font, new Vector2f(0.5f, 0f), 0.5f, false);
 				}
 			}
@@ -264,6 +264,7 @@ public class MainLoop {
 		guiRenderer.cleanUp();
 		renderer.cleanUp();
 		loader.cleanUp();
+		terrainPack.cleanUp();
 		DisplayManager.closeDisplay();
 	}
 }

+ 1 - 1
src/main/java/eu/tankernn/gameEngine/entities/PlayerCamera.java

@@ -115,7 +115,7 @@ public class PlayerCamera extends Camera {
 		float zoomLevel = Mouse.getDWheel() * 0.1f;
 		distanceFromPlayer -= zoomLevel;
 		distanceFromPlayer = Math.max(distanceFromPlayer, 10);
-		distanceFromPlayer = Math.min(distanceFromPlayer, 100);
+		distanceFromPlayer = Math.min(distanceFromPlayer, 500);
 	}
 	
 	private void calculatePitch() {

+ 13 - 33
src/main/java/eu/tankernn/gameEngine/loader/Loader.java

@@ -1,7 +1,6 @@
 package eu.tankernn.gameEngine.loader;
 
 import java.nio.FloatBuffer;
-import java.nio.IntBuffer;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -12,10 +11,10 @@ import org.lwjgl.opengl.GL20;
 import org.lwjgl.opengl.GL30;
 import org.lwjgl.opengl.GL33;
 
-import eu.tankernn.gameEngine.loader.models.RawModel;
 import eu.tankernn.gameEngine.loader.obj.ModelData;
 import eu.tankernn.gameEngine.loader.obj.normalMapped.NormalMappedObjLoader;
 import eu.tankernn.gameEngine.loader.textures.Texture;
+import eu.tankernn.gameEngine.renderEngine.RawModel;
 import eu.tankernn.gameEngine.util.InternalFile;
 /**
  * General purpose loader
@@ -25,27 +24,21 @@ import eu.tankernn.gameEngine.util.InternalFile;
 public class Loader {
 	private List<Integer> vaos = new ArrayList<Integer>();
 	private List<Integer> vbos = new ArrayList<Integer>();
+	private List<RawModel> rawModels = new ArrayList<RawModel>();
 	private List<Texture> textures = new ArrayList<Texture>();
 	
 	public RawModel loadToVAO(float[] vertices, float[] textureCoords, float[] normals, int[] indices) {
-		int vaoID = createVAO();
-		bindIndicesBuffer(indices);
-		storeDataInAttributeList(0, 3, vertices);
-		storeDataInAttributeList(1, 2, textureCoords);
-		storeDataInAttributeList(2, 3, normals);
-		unbindVAO();
-		return new RawModel(vaoID, indices.length);
+		RawModel model = RawModel.create();
+		model.storeData(indices, vertices.length / 3, vertices, textureCoords, normals);
+		rawModels.add(model);
+		return model;
 	}
 	
 	public RawModel loadToVAO(float[] vertices, float[] textureCoords, float[] normals, float[] tangents, int[] indices) {
-		int vaoID = createVAO();
-		bindIndicesBuffer(indices);
-		storeDataInAttributeList(0, 3, vertices);
-		storeDataInAttributeList(1, 2, textureCoords);
-		storeDataInAttributeList(2, 3, normals);
-		storeDataInAttributeList(3, 3, tangents);
-		unbindVAO();
-		return new RawModel(vaoID, indices.length);
+		RawModel model = RawModel.create();
+		model.storeData(indices, vertices.length / 3, vertices, textureCoords, normals, tangents);
+		rawModels.add(model);
+		return model;
 	}
 	
 	public int createEmptyVBO(int floatCount) {
@@ -99,7 +92,7 @@ public class Loader {
 	}
 	
 	public RawModel loadToVAO(ModelData data) {
-		return loadToVAO(data.getVertices(), data.getTextureCoords(), data.getNormals(), data.getIndices());
+		return (RawModel) loadToVAO(data.getVertices(), data.getTextureCoords(), data.getNormals(), data.getIndices());
 	}
 	/**
 	 * Loads a texture to the GPU.
@@ -138,6 +131,8 @@ public class Loader {
 			GL15.glDeleteBuffers(vbo);
 		for (Texture tex: textures)
 			tex.delete();
+		for (RawModel model : rawModels)
+			model.delete();
 	}
 	
 	private int createVAO() {
@@ -161,21 +156,6 @@ public class Loader {
 		GL30.glBindVertexArray(0);
 	}
 	
-	private void bindIndicesBuffer(int[] indices) {
-		int vboID = GL15.glGenBuffers();
-		vbos.add(vboID);
-		GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, vboID);
-		IntBuffer buffer = storeDataInIntBuffer(indices);
-		GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, buffer, GL15.GL_STATIC_DRAW);
-	}
-	
-	private IntBuffer storeDataInIntBuffer(int[] data) {
-		IntBuffer buffer = BufferUtils.createIntBuffer(data.length);
-		buffer.put(data);
-		buffer.flip();
-		return buffer;
-	}
-	
 	private FloatBuffer storeDataInFloatBuffer(float[] data) {
 		FloatBuffer buffer = BufferUtils.createFloatBuffer(data.length);
 		buffer.put(data);

+ 0 - 26
src/main/java/eu/tankernn/gameEngine/loader/models/RawModel.java

@@ -1,26 +0,0 @@
-package eu.tankernn.gameEngine.loader.models;
-
-public class RawModel {
-	
-	private int vaoID;
-	private int vertexCount;
-	//private AABB boundingBox;
-	
-	public RawModel(int vaoID, int vertexCount) {
-		this.vaoID = vaoID;
-		this.vertexCount = vertexCount;
-		//this.boundingBox = boundingBox;
-	}
-
-	public int getVaoID() {
-		return vaoID;
-	}
-
-	public int getVertexCount() {
-		return vertexCount;
-	}
-	
-//	public AABB getBoundingBox() {
-//		return boundingBox;
-//	}
-}

+ 1 - 0
src/main/java/eu/tankernn/gameEngine/loader/models/TexturedModel.java

@@ -1,6 +1,7 @@
 package eu.tankernn.gameEngine.loader.models;
 
 import eu.tankernn.gameEngine.loader.textures.ModelTexture;
+import eu.tankernn.gameEngine.renderEngine.RawModel;
 
 public class TexturedModel {
 	private RawModel rawModel;

+ 1 - 1
src/main/java/eu/tankernn/gameEngine/loader/obj/normalMapped/NormalMappedObjLoader.java

@@ -9,7 +9,7 @@ import org.lwjgl.util.vector.Vector2f;
 import org.lwjgl.util.vector.Vector3f;
 
 import eu.tankernn.gameEngine.loader.Loader;
-import eu.tankernn.gameEngine.loader.models.RawModel;
+import eu.tankernn.gameEngine.renderEngine.RawModel;
 import eu.tankernn.gameEngine.util.InternalFile;
 
 public class NormalMappedObjLoader {

+ 5 - 5
src/main/java/eu/tankernn/gameEngine/particles/ParticleRenderer.java

@@ -14,7 +14,7 @@ import org.lwjgl.util.vector.Vector3f;
 
 import eu.tankernn.gameEngine.entities.Camera;
 import eu.tankernn.gameEngine.loader.Loader;
-import eu.tankernn.gameEngine.loader.models.RawModel;
+import eu.tankernn.gameEngine.renderEngine.RawModel;
 
 public class ParticleRenderer {
 	
@@ -36,8 +36,8 @@ public class ParticleRenderer {
 		this.vbo = loader.createEmptyVBO(INSTANCE_DATA_LENGTH * MAX_INSTANCES);
 		quad = loader.loadToVAO(VERTICES, 2);
 		for (int i = 0; i < 5; i++)
-			loader.addInstacedAttribute(quad.getVaoID(), vbo, i + 1, 4, INSTANCE_DATA_LENGTH, i * 4);
-		loader.addInstacedAttribute(quad.getVaoID(), vbo, 6, 1, INSTANCE_DATA_LENGTH, 20);
+			loader.addInstacedAttribute(quad.id, vbo, i + 1, 4, INSTANCE_DATA_LENGTH, i * 4);
+		loader.addInstacedAttribute(quad.id, vbo, 6, 1, INSTANCE_DATA_LENGTH, 20);
 		shader = new ParticleShader();
 		shader.start();
 		shader.projectionMatrix.loadMatrix(projectionMatrix);
@@ -57,7 +57,7 @@ public class ParticleRenderer {
 				updateTexCoordInfo(p, vboData);
 			}
 			loader.updateVBO(vbo, vboData, buffer);
-			GL31.glDrawArraysInstanced(GL11.GL_TRIANGLE_STRIP, 0, quad.getVertexCount(), particleList.size());
+			GL31.glDrawArraysInstanced(GL11.GL_TRIANGLE_STRIP, 0, quad.getIndexCount(), particleList.size());
 		}
 		finishRendering();
 	}
@@ -122,7 +122,7 @@ public class ParticleRenderer {
 	
 	private void prepare() {
 		shader.start();
-		GL30.glBindVertexArray(quad.getVaoID());
+		GL30.glBindVertexArray(quad.id);
 		for (int i = 0; i < 7; i++)
 			GL20.glEnableVertexAttribArray(i);
 		GL11.glEnable(GL11.GL_BLEND);

+ 3 - 7
src/main/java/eu/tankernn/gameEngine/postProcessing/PostProcessing.java

@@ -2,16 +2,14 @@ package eu.tankernn.gameEngine.postProcessing;
 
 import org.lwjgl.opengl.Display;
 import org.lwjgl.opengl.GL11;
-import org.lwjgl.opengl.GL20;
-import org.lwjgl.opengl.GL30;
 
 import eu.tankernn.gameEngine.loader.Loader;
-import eu.tankernn.gameEngine.loader.models.RawModel;
 import eu.tankernn.gameEngine.loader.textures.Texture;
 import eu.tankernn.gameEngine.postProcessing.bloom.BrightFilter;
 import eu.tankernn.gameEngine.postProcessing.bloom.CombineFilter;
 import eu.tankernn.gameEngine.postProcessing.gaussianBlur.HorizontalBlur;
 import eu.tankernn.gameEngine.postProcessing.gaussianBlur.VerticalBlur;
+import eu.tankernn.gameEngine.renderEngine.RawModel;
 
 public class PostProcessing {
 	
@@ -62,15 +60,13 @@ public class PostProcessing {
 	}
 	
 	private static void start() {
-		GL30.glBindVertexArray(quad.getVaoID());
-		GL20.glEnableVertexAttribArray(0);
+		quad.bind(0);
 		GL11.glDisable(GL11.GL_DEPTH_TEST);
 	}
 	
 	private static void end() {
 		GL11.glEnable(GL11.GL_DEPTH_TEST);
-		GL20.glDisableVertexAttribArray(0);
-		GL30.glBindVertexArray(0);
+		quad.unbind(0);
 	}
 	
 }

+ 11 - 5
src/main/java/eu/tankernn/gameEngine/renderEngine/Vao.java → src/main/java/eu/tankernn/gameEngine/renderEngine/RawModel.java

@@ -5,7 +5,7 @@ import org.lwjgl.opengl.GL15;
 import org.lwjgl.opengl.GL20;
 import org.lwjgl.opengl.GL30;
 
-public class Vao {
+public class RawModel {
 	
 	private static final int BYTES_PER_FLOAT = 4;
 
@@ -14,15 +14,20 @@ public class Vao {
 	private Vbo indexVbo;
 	private int indexCount;
 
-	public static Vao create() {
+	public static RawModel create() {
 		int id = GL30.glGenVertexArrays();
-		return new Vao(id);
+		return new RawModel(id);
 	}
 
-	private Vao(int id) {
+	private RawModel(int id) {
 		this.id = id;
 	}
 	
+	public RawModel(int id, int indexCount) {
+		this(id);
+		this.indexCount = indexCount;
+	}
+	
 	public int getIndexCount(){
 		return indexCount;
 	}
@@ -49,11 +54,12 @@ public class Vao {
 		unbind();
 	}
 	
-	public void storeData(int[] indices, int vertexCount, float[]... data){
+	public RawModel storeData(int[] indices, int vertexCount, float[]... data){
 		bind();
 		storeData(vertexCount, data);
 		createIndexBuffer(indices);
 		unbind();
+		return this;
 	}
 	
 	public void delete() {

+ 5 - 13
src/main/java/eu/tankernn/gameEngine/renderEngine/entities/EntityRenderer.java

@@ -8,8 +8,6 @@ import java.util.List;
 import java.util.Map;
 
 import org.lwjgl.opengl.GL11;
-import org.lwjgl.opengl.GL20;
-import org.lwjgl.opengl.GL30;
 import org.lwjgl.util.vector.Matrix4f;
 import org.lwjgl.util.vector.Vector4f;
 
@@ -71,9 +69,9 @@ public class EntityRenderer {
 			List<Entity> batch = entities.get(model);
 			for (Entity entity : batch) {
 				prepareInstance(entity);
-				GL11.glDrawElements(GL11.GL_TRIANGLES, model.getRawModel().getVertexCount(), GL11.GL_UNSIGNED_INT, 0);
+				GL11.glDrawElements(GL11.GL_TRIANGLES, model.getRawModel().getIndexCount(), GL11.GL_UNSIGNED_INT, 0);
 			}
-			unbindTexturedModel();
+			unbindTexturedModel(model);
 		}
 		shader.stop();
 	}
@@ -83,10 +81,7 @@ public class EntityRenderer {
 	}
 
 	private void prepareTexturedModel(TexturedModel model, Texture environmentMap) {
-		GL30.glBindVertexArray(model.getRawModel().getVaoID());
-		GL20.glEnableVertexAttribArray(0);
-		GL20.glEnableVertexAttribArray(1);
-		GL20.glEnableVertexAttribArray(2);
+		model.getRawModel().bind(0, 1, 2);
 		ModelTexture texture = model.getModelTexture();
 		shader.numberOfRows.loadFloat(texture.getNumberOfRows());
 		if (texture.hasTransparency())
@@ -106,12 +101,9 @@ public class EntityRenderer {
 		environmentMap.bindToUnit(10);
 	}
 
-	private void unbindTexturedModel() {
+	private void unbindTexturedModel(TexturedModel model) {
 		MasterRenderer.enableCulling();
-		GL20.glDisableVertexAttribArray(0);
-		GL20.glDisableVertexAttribArray(1);
-		GL20.glDisableVertexAttribArray(2);
-		GL30.glBindVertexArray(0);
+		model.getRawModel().unbind(0, 1, 2);
 	}
 
 	private void prepareInstance(Entity entity) {

+ 4 - 8
src/main/java/eu/tankernn/gameEngine/renderEngine/gui/GuiRenderer.java

@@ -4,12 +4,10 @@ import java.util.List;
 
 import org.lwjgl.opengl.GL11;
 import org.lwjgl.opengl.GL13;
-import org.lwjgl.opengl.GL20;
-import org.lwjgl.opengl.GL30;
 import org.lwjgl.util.vector.Matrix4f;
 
 import eu.tankernn.gameEngine.loader.Loader;
-import eu.tankernn.gameEngine.loader.models.RawModel;
+import eu.tankernn.gameEngine.renderEngine.RawModel;
 import eu.tankernn.gameEngine.util.Maths;
 
 public class GuiRenderer {
@@ -25,8 +23,7 @@ public class GuiRenderer {
 	
 	public void render(List<GuiTexture> guis) {
 		shader.start();
-		GL30.glBindVertexArray(quad.getVaoID());
-		GL20.glEnableVertexAttribArray(0);
+		quad.bind(0);
 		GL11.glEnable(GL11.GL_BLEND);
 		GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
 		GL11.glDisable(GL11.GL_DEPTH_TEST);
@@ -35,12 +32,11 @@ public class GuiRenderer {
 			GL11.glBindTexture(GL11.GL_TEXTURE_2D, gui.getTexture());
 			Matrix4f matrix = Maths.createTransformationMatrix(gui.getPosition(), gui.getScale());
 			shader.transformationMatrix.loadMatrix(matrix);
-			GL11.glDrawArrays(GL11.GL_TRIANGLE_STRIP, 0, quad.getVertexCount());
+			GL11.glDrawArrays(GL11.GL_TRIANGLE_STRIP, 0, quad.getIndexCount());
 		}
 		GL11.glEnable(GL11.GL_DEPTH_TEST);
 		GL11.glDisable(GL11.GL_BLEND);
-		GL20.glDisableVertexAttribArray(0);
-		GL30.glBindVertexArray(0);
+		quad.unbind(0);
 		shader.stop();
 	}
 	

+ 12 - 21
src/main/java/eu/tankernn/gameEngine/renderEngine/normalMap/NormalMappingRenderer.java

@@ -4,17 +4,15 @@ import java.util.List;
 import java.util.Map;
 
 import org.lwjgl.opengl.GL11;
-import org.lwjgl.opengl.GL20;
-import org.lwjgl.opengl.GL30;
 import org.lwjgl.util.vector.Matrix4f;
 import org.lwjgl.util.vector.Vector4f;
 
 import eu.tankernn.gameEngine.entities.Entity;
 import eu.tankernn.gameEngine.entities.Light;
-import eu.tankernn.gameEngine.loader.models.RawModel;
 import eu.tankernn.gameEngine.loader.models.TexturedModel;
 import eu.tankernn.gameEngine.loader.textures.ModelTexture;
 import eu.tankernn.gameEngine.renderEngine.MasterRenderer;
+import eu.tankernn.gameEngine.renderEngine.RawModel;
 import eu.tankernn.gameEngine.settings.Settings;
 import eu.tankernn.gameEngine.util.ICamera;
 import eu.tankernn.gameEngine.util.Maths;
@@ -31,7 +29,8 @@ public class NormalMappingRenderer {
 		shader.stop();
 	}
 
-	public void render(Map<TexturedModel, List<Entity>> entities, Vector4f clipPlane, List<Light> lights, ICamera camera) {
+	public void render(Map<TexturedModel, List<Entity>> entities, Vector4f clipPlane, List<Light> lights,
+			ICamera camera) {
 		shader.start();
 		prepare(clipPlane, lights, camera);
 		for (TexturedModel model : entities.keySet()) {
@@ -39,24 +38,20 @@ public class NormalMappingRenderer {
 			List<Entity> batch = entities.get(model);
 			for (Entity entity : batch) {
 				prepareInstance(entity);
-				GL11.glDrawElements(GL11.GL_TRIANGLES, model.getRawModel().getVertexCount(), GL11.GL_UNSIGNED_INT, 0);
+				GL11.glDrawElements(GL11.GL_TRIANGLES, model.getRawModel().getIndexCount(), GL11.GL_UNSIGNED_INT, 0);
 			}
-			unbindTexturedModel();
+			unbindTexturedModel(model);
 		}
 		shader.stop();
 	}
-	
-	public void cleanUp(){
+
+	public void cleanUp() {
 		shader.cleanUp();
 	}
 
 	private void prepareTexturedModel(TexturedModel model) {
 		RawModel rawModel = model.getRawModel();
-		GL30.glBindVertexArray(rawModel.getVaoID());
-		GL20.glEnableVertexAttribArray(0);
-		GL20.glEnableVertexAttribArray(1);
-		GL20.glEnableVertexAttribArray(2);
-		GL20.glEnableVertexAttribArray(3);
+		rawModel.bind(0, 1, 2, 3);
 		ModelTexture texture = model.getModelTexture();
 		shader.numberOfRows.loadFloat(texture.getNumberOfRows());
 		if (texture.hasTransparency()) {
@@ -72,13 +67,9 @@ public class NormalMappingRenderer {
 		}
 	}
 
-	private void unbindTexturedModel() {
+	private void unbindTexturedModel(TexturedModel model) {
 		MasterRenderer.enableCulling();
-		GL20.glDisableVertexAttribArray(0);
-		GL20.glDisableVertexAttribArray(1);
-		GL20.glDisableVertexAttribArray(2);
-		GL20.glDisableVertexAttribArray(3);
-		GL30.glBindVertexArray(0);
+		model.getRawModel().unbind(0, 1, 2, 3);
 	}
 
 	private void prepareInstance(Entity entity) {
@@ -90,10 +81,10 @@ public class NormalMappingRenderer {
 
 	private void prepare(Vector4f clipPlane, List<Light> lights, ICamera camera) {
 		shader.plane.loadVec4(clipPlane);
-		//need to be public variables in MasterRenderer
+		// need to be public variables in MasterRenderer
 		shader.skyColour.loadVec3(Settings.RED, Settings.GREEN, Settings.BLUE);
 		Matrix4f viewMatrix = camera.getViewMatrix();
-		
+
 		shader.loadLights(lights, viewMatrix);
 		shader.viewMatrix.loadMatrix(viewMatrix);
 	}

+ 5 - 8
src/main/java/eu/tankernn/gameEngine/renderEngine/shadows/ShadowMapEntityRenderer.java

@@ -9,9 +9,9 @@ import org.lwjgl.opengl.GL30;
 import org.lwjgl.util.vector.Matrix4f;
 
 import eu.tankernn.gameEngine.entities.Entity;
-import eu.tankernn.gameEngine.loader.models.RawModel;
 import eu.tankernn.gameEngine.loader.models.TexturedModel;
 import eu.tankernn.gameEngine.renderEngine.MasterRenderer;
+import eu.tankernn.gameEngine.renderEngine.RawModel;
 import eu.tankernn.gameEngine.util.Maths;
 
 public class ShadowMapEntityRenderer {
@@ -49,8 +49,7 @@ public class ShadowMapEntityRenderer {
 			}
 			for (Entity entity : entities.get(model)) {
 				prepareInstance(entity);
-				GL11.glDrawElements(GL11.GL_TRIANGLES, rawModel.getVertexCount(),
-						GL11.GL_UNSIGNED_INT, 0);
+				GL11.glDrawElements(GL11.GL_TRIANGLES, rawModel.getIndexCount(), GL11.GL_UNSIGNED_INT, 0);
 			}
 			if (model.getModelTexture().hasTransparency()) {
 				MasterRenderer.enableCulling();
@@ -70,9 +69,7 @@ public class ShadowMapEntityRenderer {
 	 *            - the model to be bound.
 	 */
 	private void bindModel(RawModel rawModel) {
-		GL30.glBindVertexArray(rawModel.getVaoID());
-		GL20.glEnableVertexAttribArray(0);
-		GL20.glEnableVertexAttribArray(1);
+		rawModel.bind(0, 1);
 	}
 
 	/**
@@ -85,8 +82,8 @@ public class ShadowMapEntityRenderer {
 	 *            - the entity to be prepared for rendering.
 	 */
 	private void prepareInstance(Entity entity) {
-		Matrix4f modelMatrix = Maths.createTransformationMatrix(entity.getPosition(),
-				entity.getRotX(), entity.getRotY(), entity.getRotZ(), entity.getScale());
+		Matrix4f modelMatrix = Maths.createTransformationMatrix(entity.getPosition(), entity.getRotX(),
+				entity.getRotY(), entity.getRotZ(), entity.getScale());
 		Matrix4f mvpMatrix = Matrix4f.mul(projectionViewMatrix, modelMatrix, null);
 		shader.mvpMatrix.loadMatrix(mvpMatrix);
 	}

+ 3 - 3
src/main/java/eu/tankernn/gameEngine/renderEngine/skybox/CubeGenerator.java

@@ -1,6 +1,6 @@
 package eu.tankernn.gameEngine.renderEngine.skybox;
 
-import eu.tankernn.gameEngine.renderEngine.Vao;
+import eu.tankernn.gameEngine.renderEngine.RawModel;
 
 public class CubeGenerator {
 
@@ -8,8 +8,8 @@ public class CubeGenerator {
 	private static final int[] INDICES = { 0, 1, 3, 1, 2, 3, 1, 5, 2, 2, 5, 6, 4, 7, 5, 5, 7, 6, 0,
 			3, 4, 4, 3, 7, 7, 3, 6, 6, 3, 2, 4, 5, 0, 0, 5, 1 };
 
-	public static Vao generateCube(float size) {
-		Vao vao = Vao.create();
+	public static RawModel generateCube(float size) {
+		RawModel vao = RawModel.create();
 		vao.storeData(INDICES, VERTEX_COUNT, getVertexPositions(size));
 		return vao;
 	}

+ 3 - 3
src/main/java/eu/tankernn/gameEngine/renderEngine/skybox/Skybox.java

@@ -1,11 +1,11 @@
 package eu.tankernn.gameEngine.renderEngine.skybox;
 
 import eu.tankernn.gameEngine.loader.textures.Texture;
-import eu.tankernn.gameEngine.renderEngine.Vao;
+import eu.tankernn.gameEngine.renderEngine.RawModel;
 
 public class Skybox {
 	
-	private Vao cube;
+	private RawModel cube;
 	private Texture dayTexture, nightTexture;
 	
 	public Skybox(Texture dayTexture, Texture nightTexture, float size){
@@ -14,7 +14,7 @@ public class Skybox {
 		this.nightTexture = nightTexture;
 	}
 	
-	public Vao getCubeVao(){
+	public RawModel getCubeVao(){
 		return cube;
 	}
 	

+ 2 - 2
src/main/java/eu/tankernn/gameEngine/renderEngine/skybox/SkyboxRenderer.java

@@ -8,7 +8,7 @@ import org.lwjgl.util.vector.Matrix4f;
 import eu.tankernn.gameEngine.loader.Loader;
 import eu.tankernn.gameEngine.loader.textures.Texture;
 import eu.tankernn.gameEngine.renderEngine.DisplayManager;
-import eu.tankernn.gameEngine.renderEngine.Vao;
+import eu.tankernn.gameEngine.renderEngine.RawModel;
 import eu.tankernn.gameEngine.util.ICamera;
 
 public class SkyboxRenderer {
@@ -34,7 +34,7 @@ public class SkyboxRenderer {
 		skybox.getCubeVao().bind();
 		GL20.glEnableVertexAttribArray(0);
 		bindTextures();
-		Vao model = skybox.getCubeVao();
+		RawModel model = skybox.getCubeVao();
 		model.bind(0);
 		GL11.glDrawElements(GL11.GL_TRIANGLES, model.getIndexCount(), GL11.GL_UNSIGNED_INT, 0);
 		model.unbind(0);

+ 5 - 13
src/main/java/eu/tankernn/gameEngine/renderEngine/terrain/TerrainRenderer.java

@@ -7,8 +7,6 @@ import static eu.tankernn.gameEngine.settings.Settings.RED;
 import java.util.List;
 
 import org.lwjgl.opengl.GL11;
-import org.lwjgl.opengl.GL20;
-import org.lwjgl.opengl.GL30;
 import org.lwjgl.util.vector.Matrix4f;
 import org.lwjgl.util.vector.Vector3f;
 import org.lwjgl.util.vector.Vector4f;
@@ -45,9 +43,9 @@ public class TerrainRenderer {
 		for (Terrain terrain : terrains) {
 			prepareTerrain(terrain);
 			loadModelMatrix(terrain);
-			GL11.glDrawElements(GL11.GL_TRIANGLES, terrain.getModel().getVertexCount(), GL11.GL_UNSIGNED_INT, 0);
+			GL11.glDrawElements(GL11.GL_TRIANGLES, terrain.getModel().getIndexCount(), GL11.GL_UNSIGNED_INT, 0);
 
-			unbindTexturedModel();
+			unbindTexturedModel(terrain);
 		}
 		shader.stop();
 	}
@@ -57,10 +55,7 @@ public class TerrainRenderer {
 	}
 
 	private void prepareTerrain(Terrain terrain) {
-		GL30.glBindVertexArray(terrain.getModel().getVaoID());
-		GL20.glEnableVertexAttribArray(0);
-		GL20.glEnableVertexAttribArray(1);
-		GL20.glEnableVertexAttribArray(2);
+		terrain.getModel().bind(0, 1, 2);
 		bindTexture(terrain);
 		shader.shineDamper.loadFloat(1); // No shine
 		shader.reflectivity.loadFloat(0); // No shine
@@ -76,11 +71,8 @@ public class TerrainRenderer {
 		terrain.getBlendMap().bindToUnit(4);
 	}
 
-	private void unbindTexturedModel() {
-		GL20.glDisableVertexAttribArray(0);
-		GL20.glDisableVertexAttribArray(1);
-		GL20.glDisableVertexAttribArray(2);
-		GL30.glBindVertexArray(0);
+	private void unbindTexturedModel(Terrain terrain) {
+		terrain.getModel().unbind(0, 1, 2);
 	}
 
 	private void loadModelMatrix(Terrain terrain) {

+ 3 - 4
src/main/java/eu/tankernn/gameEngine/renderEngine/water/WaterRenderer.java

@@ -11,9 +11,9 @@ import org.lwjgl.util.vector.Vector3f;
 import eu.tankernn.gameEngine.entities.Camera;
 import eu.tankernn.gameEngine.entities.Light;
 import eu.tankernn.gameEngine.loader.Loader;
-import eu.tankernn.gameEngine.loader.models.RawModel;
 import eu.tankernn.gameEngine.loader.textures.Texture;
 import eu.tankernn.gameEngine.renderEngine.DisplayManager;
+import eu.tankernn.gameEngine.renderEngine.RawModel;
 import eu.tankernn.gameEngine.util.Maths;
 
 public class WaterRenderer {
@@ -48,7 +48,7 @@ public class WaterRenderer {
 					new Vector3f(tile.getX(), tile.getHeight(), tile.getZ()), 0, 0, 0,
 					WaterTile.TILE_SIZE);
 			shader.modelMatrix.loadMatrix(modelMatrix);
-			GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, quad.getVertexCount());
+			GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, quad.getIndexCount());
 		}
 		unbind();
 	}
@@ -61,8 +61,7 @@ public class WaterRenderer {
 		moveFactor %= 1;
 		shader.moveFactor.loadFloat(moveFactor);
 		shader.loadLights(lights);
-		GL30.glBindVertexArray(quad.getVaoID());
-		GL20.glEnableVertexAttribArray(0);
+		quad.bind(0);
 		buffers.getReflectionTexture().bindToUnit(0);
 		buffers.getRefractionTexture().bindToUnit(1);
 		dudvTexture.bindToUnit(2);

+ 2 - 0
src/main/java/eu/tankernn/gameEngine/settings/Settings.java

@@ -11,5 +11,7 @@ public class Settings {
 	public static final float GREEN = 0.75f;
 	public static final float BLUE = 0.8f;
 	
+	public static final float TERRAIN_SIZE = 800;
+	
 	public static final float ANISOTROPIC_FILTERING_AMOUNT = 4f;
 }

+ 61 - 174
src/main/java/eu/tankernn/gameEngine/terrains/Terrain.java

@@ -1,235 +1,122 @@
 package eu.tankernn.gameEngine.terrains;
 
-import java.awt.image.BufferedImage;
-import java.io.File;
-import java.io.IOException;
-
-import javax.imageio.ImageIO;
+import static eu.tankernn.gameEngine.settings.Settings.TERRAIN_SIZE;
 
+import org.apache.commons.lang3.tuple.Pair;
 import org.lwjgl.util.vector.Vector2f;
 import org.lwjgl.util.vector.Vector3f;
 
 import eu.tankernn.gameEngine.loader.Loader;
-import eu.tankernn.gameEngine.loader.models.RawModel;
 import eu.tankernn.gameEngine.loader.textures.TerrainTexturePack;
 import eu.tankernn.gameEngine.loader.textures.Texture;
+import eu.tankernn.gameEngine.renderEngine.RawModel;
+import eu.tankernn.gameEngine.util.InternalFile;
 import eu.tankernn.gameEngine.util.Maths;
 
 public class Terrain {
-	private static final float SIZE = 800;
-	private static final float MAX_HEIGHT = 40;
-	private static final float MAX_PIXEL_COLOR = 256 * 256 * 256;
-	
+
 	private float x, z;
 	private int gridX, gridZ;
 	private RawModel model;
 	private TerrainTexturePack texturePack;
 	private Texture blendMap;
-	
+
 	private float[][] heights;
-	
-	public Terrain(int gridX, int gridZ, Loader loader, TerrainTexturePack texturePack, Texture blendMap, String heightMap) {
-		this.texturePack = texturePack;
-		this.blendMap = blendMap;
+
+	public Terrain(int gridX, int gridZ, Loader loader, TerrainTexturePack texturePack, Texture blendMap,
+			InternalFile heightMap) {
 		this.gridX = gridX;
 		this.gridZ = gridZ;
-		this.x = gridX * SIZE;
-		this.z = gridZ * SIZE;
-		this.model = generateTerrain(loader, heightMap);
+		this.texturePack = texturePack;
+		this.blendMap = blendMap;
+		this.x = gridX * TERRAIN_SIZE;
+		this.z = gridZ * TERRAIN_SIZE;
+		TerrainModelData data = new TerrainModelData(gridX, gridZ, heightMap);
+		this.model = data.getModel(loader);
+		this.heights = data.getHeights();
 	}
-	
+
 	public Terrain(int gridX, int gridZ, Loader loader, TerrainTexturePack texturePack, Texture blendMap, int seed) {
+		this.gridX = gridX;
+		this.gridZ = gridZ;
 		this.texturePack = texturePack;
 		this.blendMap = blendMap;
+		this.x = gridX * TERRAIN_SIZE;
+		this.z = gridZ * TERRAIN_SIZE;
+		TerrainModelData data = new TerrainModelData(gridX, gridZ, seed);
+		this.model = data.getModel(loader);
+		this.heights = data.getHeights();
+	}
+
+	public Terrain(int gridX, int gridZ, Loader loader, TerrainTexturePack texturePack, Texture blendMap,
+			TerrainModelData data) {
 		this.gridX = gridX;
 		this.gridZ = gridZ;
-		this.x = gridX * SIZE;
-		this.z = gridZ * SIZE;
-		this.model = generateTerrain(loader, seed);
-	}
-	
-	private RawModel generateTerrain(Loader loader, String heightMap) {
-		
-		BufferedImage image = null;
-		try {
-			image = ImageIO.read(new File("res/" + heightMap + ".png"));
-		} catch (IOException e) {
-			e.printStackTrace();
-		}
-		
-		int vertexCount = image.getHeight();
-		heights = new float[vertexCount][vertexCount];
-		int count = vertexCount * vertexCount;
-		float[] vertices = new float[count * 3];
-		float[] normals = new float[count * 3];
-		float[] textureCoords = new float[count * 2];
-		int[] indices = new int[6 * (vertexCount - 1) * (vertexCount - 1)];
-		int vertexPointer = 0;
-		for (int i = 0; i < vertexCount; i++) {
-			for (int j = 0; j < vertexCount; j++) {
-				vertices[vertexPointer * 3] = (float) j / ((float) vertexCount - 1) * SIZE;
-				float height = getHeight(j, i, image);
-				heights[j][i] = height;
-				vertices[vertexPointer * 3 + 1] = height;
-				vertices[vertexPointer * 3 + 2] = (float) i / ((float) vertexCount - 1) * SIZE;
-				Vector3f normal = calculateNormal(j, i, image);
-				normals[vertexPointer * 3] = normal.x;
-				normals[vertexPointer * 3 + 1] = normal.y;
-				normals[vertexPointer * 3 + 2] = normal.z;
-				textureCoords[vertexPointer * 2] = (float) j / ((float) vertexCount - 1);
-				textureCoords[vertexPointer * 2 + 1] = (float) i / ((float) vertexCount - 1);
-				vertexPointer++;
-			}
-		}
-		int pointer = 0;
-		for (int gz = 0; gz < vertexCount - 1; gz++) {
-			for (int gx = 0; gx < vertexCount - 1; gx++) {
-				int topLeft = (gz * vertexCount) + gx;
-				int topRight = topLeft + 1;
-				int bottomLeft = ((gz + 1) * vertexCount) + gx;
-				int bottomRight = bottomLeft + 1;
-				indices[pointer++] = topLeft;
-				indices[pointer++] = bottomLeft;
-				indices[pointer++] = topRight;
-				indices[pointer++] = topRight;
-				indices[pointer++] = bottomLeft;
-				indices[pointer++] = bottomRight;
-			}
-		}
-		return loader.loadToVAO(vertices, textureCoords, normals, indices);
-	}
-	
-	private RawModel generateTerrain(Loader loader, int seed) {
-		int vertexCount = 128;
-		HeightsGenerator generator = new HeightsGenerator(this.gridX, this.gridZ, vertexCount, seed);
-		
-		heights = new float[vertexCount][vertexCount];
-		int count = vertexCount * vertexCount;
-		float[] vertices = new float[count * 3];
-		float[] normals = new float[count * 3];
-		float[] textureCoords = new float[count * 2];
-		int[] indices = new int[6 * (vertexCount - 1) * (vertexCount - 1)];
-		int vertexPointer = 0;
-		for (int i = 0; i < vertexCount; i++) {
-			for (int j = 0; j < vertexCount; j++) {
-				vertices[vertexPointer * 3] = (float) j / ((float) vertexCount - 1) * SIZE;
-				float height = getHeight(j, i, generator);
-				heights[j][i] = height;
-				vertices[vertexPointer * 3 + 1] = height;
-				vertices[vertexPointer * 3 + 2] = (float) i / ((float) vertexCount - 1) * SIZE;
-				Vector3f normal = calculateNormal(j, i, generator);
-				normals[vertexPointer * 3] = normal.x;
-				normals[vertexPointer * 3 + 1] = normal.y;
-				normals[vertexPointer * 3 + 2] = normal.z;
-				textureCoords[vertexPointer * 2] = (float) j / ((float) vertexCount - 1);
-				textureCoords[vertexPointer * 2 + 1] = (float) i / ((float) vertexCount - 1);
-				vertexPointer++;
-			}
-		}
-		int pointer = 0;
-		for (int gz = 0; gz < vertexCount - 1; gz++) {
-			for (int gx = 0; gx < vertexCount - 1; gx++) {
-				int topLeft = (gz * vertexCount) + gx;
-				int topRight = topLeft + 1;
-				int bottomLeft = ((gz + 1) * vertexCount) + gx;
-				int bottomRight = bottomLeft + 1;
-				indices[pointer++] = topLeft;
-				indices[pointer++] = bottomLeft;
-				indices[pointer++] = topRight;
-				indices[pointer++] = topRight;
-				indices[pointer++] = bottomLeft;
-				indices[pointer++] = bottomRight;
-			}
-		}
-		return loader.loadToVAO(vertices, textureCoords, normals, indices);
-	}
-	
-	private Vector3f calculateNormal(int x, int z, BufferedImage image) {
-		float heightL = getHeight(x - 1, z, image);
-		float heightR = getHeight(x + 1, z, image);
-		float heightD = getHeight(x, z - 1, image);
-		float heightU = getHeight(x, z + 1, image);
-		Vector3f normal = new Vector3f(heightL - heightR, 2f, heightD - heightU);
-		normal.normalise();
-		return normal;
-	}
-	
-	private Vector3f calculateNormal(int x, int z, HeightsGenerator generator) {
-		float heightL = getHeight(x - 1, z, generator);
-		float heightR = getHeight(x + 1, z, generator);
-		float heightD = getHeight(x, z - 1, generator);
-		float heightU = getHeight(x, z + 1, generator);
-		Vector3f normal = new Vector3f(heightL - heightR, 2f, heightD - heightU);
-		normal.normalise();
-		return normal;
-	}
-	
-	private float getHeight(int x, int z, BufferedImage image) {
-		if (x < 0 || x >= image.getHeight() || z < 0 || z >= image.getHeight()) {
-			return 0;
-		}
-		float height = image.getRGB(x, z);
-		height += MAX_PIXEL_COLOR / 2f;
-		height /= MAX_PIXEL_COLOR / 2f;
-		height *= MAX_HEIGHT;
-		return height;
-	}
-	
-	private float getHeight(int x, int z, HeightsGenerator generator) {
-		return generator.generateHeight(x, z);
+		this.texturePack = texturePack;
+		this.blendMap = blendMap;
+		this.x = gridX * TERRAIN_SIZE;
+		this.z = gridZ * TERRAIN_SIZE;
+		this.model = data.getModel(loader);
+		this.heights = data.getHeights();
 	}
-	
+
 	public float getX() {
 		return x;
 	}
-	
+
 	public float getZ() {
 		return z;
 	}
-	
+
 	float getSize() {
-		return SIZE;
+		return TERRAIN_SIZE;
 	}
-	
+
 	public RawModel getModel() {
 		return model;
 	}
-	
+
 	public TerrainTexturePack getTexturePack() {
 		return texturePack;
 	}
-	
+
 	public Texture getBlendMap() {
 		return blendMap;
 	}
-	
+
+	public Pair<Integer, Integer> getGridPosition() {
+		return Pair.of(this.gridX, this.gridZ);
+	}
+
 	public float getHeightOfTerrain(float worldX, float worldZ) {
 		float terrainX = worldX - this.x;
 		float terrainZ = worldZ - this.z;
-		
-		float gridSquareSize = SIZE / ((float) heights.length - 1);
+
+		float gridSquareSize = TERRAIN_SIZE / ((float) heights.length - 1);
 		int gridX = (int) Math.floor(terrainX / gridSquareSize);
 		int gridZ = (int) Math.floor(terrainZ / gridSquareSize);
-		
+
 		if (gridX >= heights.length - 1 || gridZ >= heights.length - 1 || gridX < 0 || gridZ < 0) {
 			return 0;
 		}
-		
+
 		float xCoord = (terrainX % gridSquareSize) / gridSquareSize;
 		float zCoord = (terrainZ % gridSquareSize) / gridSquareSize;
-		
+
 		float answer;
 		if (xCoord <= (1 - zCoord)) {
-			answer = Maths
-					.barryCentric(new Vector3f(0, heights[gridX][gridZ], 0), new Vector3f(1,
-							heights[gridX + 1][gridZ], 0), new Vector3f(0,
-							heights[gridX][gridZ + 1], 1), new Vector2f(xCoord, zCoord));
+			answer = Maths.barryCentric(new Vector3f(0, heights[gridX][gridZ], 0),
+					new Vector3f(1, heights[gridX + 1][gridZ], 0), new Vector3f(0, heights[gridX][gridZ + 1], 1),
+					new Vector2f(xCoord, zCoord));
 		} else {
-			answer = Maths
-					.barryCentric(new Vector3f(1, heights[gridX + 1][gridZ], 0), new Vector3f(1,
-							heights[gridX + 1][gridZ + 1], 1), new Vector3f(0,
-							heights[gridX][gridZ + 1], 1), new Vector2f(xCoord, zCoord));
+			answer = Maths.barryCentric(new Vector3f(1, heights[gridX + 1][gridZ], 0),
+					new Vector3f(1, heights[gridX + 1][gridZ + 1], 1), new Vector3f(0, heights[gridX][gridZ + 1], 1),
+					new Vector2f(xCoord, zCoord));
 		}
 		return answer;
 	}
+	
+	public void delete() {
+		model.delete();
+	}
 }

+ 174 - 0
src/main/java/eu/tankernn/gameEngine/terrains/TerrainModelData.java

@@ -0,0 +1,174 @@
+package eu.tankernn.gameEngine.terrains;
+
+import static eu.tankernn.gameEngine.settings.Settings.TERRAIN_SIZE;
+
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+
+import javax.imageio.ImageIO;
+
+import org.lwjgl.util.vector.Vector3f;
+
+import eu.tankernn.gameEngine.loader.Loader;
+import eu.tankernn.gameEngine.renderEngine.RawModel;
+import eu.tankernn.gameEngine.util.InternalFile;
+
+public class TerrainModelData {
+	private static final float MAX_HEIGHT = 40;
+	private static final float MAX_PIXEL_COLOR = 256 * 256 * 256;
+	
+	private int gridX, gridZ;
+	private float[][] heights;
+	private float[] vertices;
+	private float[] normals;
+	private float[] textureCoords;
+	private int[] indices;
+
+	public TerrainModelData(int gridX, int gridZ, int seed) {
+		this.gridX = gridX;
+		this.gridZ = gridZ;
+		int vertexCount = 128;
+		HeightsGenerator generator = new HeightsGenerator(gridX, gridZ, vertexCount, seed);
+
+		heights = new float[vertexCount][vertexCount];
+		int count = vertexCount * vertexCount;
+		vertices = new float[count * 3];
+		normals = new float[count * 3];
+		textureCoords = new float[count * 2];
+		indices = new int[6 * (vertexCount - 1) * (vertexCount - 1)];
+		int vertexPointer = 0;
+		for (int i = 0; i < vertexCount; i++) {
+			for (int j = 0; j < vertexCount; j++) {
+				vertices[vertexPointer * 3] = (float) j / ((float) vertexCount - 1) * TERRAIN_SIZE;
+				float height = getHeight(j, i, generator);
+				heights[j][i] = height;
+				vertices[vertexPointer * 3 + 1] = height;
+				vertices[vertexPointer * 3 + 2] = (float) i / ((float) vertexCount - 1) * TERRAIN_SIZE;
+				Vector3f normal = calculateNormal(j, i, generator);
+				normals[vertexPointer * 3] = normal.x;
+				normals[vertexPointer * 3 + 1] = normal.y;
+				normals[vertexPointer * 3 + 2] = normal.z;
+				textureCoords[vertexPointer * 2] = (float) j / ((float) vertexCount - 1);
+				textureCoords[vertexPointer * 2 + 1] = (float) i / ((float) vertexCount - 1);
+				vertexPointer++;
+			}
+		}
+		int pointer = 0;
+		for (int gz = 0; gz < vertexCount - 1; gz++) {
+			for (int gx = 0; gx < vertexCount - 1; gx++) {
+				int topLeft = (gz * vertexCount) + gx;
+				int topRight = topLeft + 1;
+				int bottomLeft = ((gz + 1) * vertexCount) + gx;
+				int bottomRight = bottomLeft + 1;
+				indices[pointer++] = topLeft;
+				indices[pointer++] = bottomLeft;
+				indices[pointer++] = topRight;
+				indices[pointer++] = topRight;
+				indices[pointer++] = bottomLeft;
+				indices[pointer++] = bottomRight;
+			}
+		}
+	}
+
+	public TerrainModelData(int gridX, int gridZ, InternalFile heightMap) {
+		this.gridX = gridX;
+		this.gridZ = gridZ;
+		BufferedImage image = null;
+		try {
+			image = ImageIO.read(heightMap.getInputStream());
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+
+		int vertexCount = image.getHeight();
+		heights = new float[vertexCount][vertexCount];
+		int count = vertexCount * vertexCount;
+		vertices = new float[count * 3];
+		normals = new float[count * 3];
+		textureCoords = new float[count * 2];
+		indices = new int[6 * (vertexCount - 1) * (vertexCount - 1)];
+		int vertexPointer = 0;
+		for (int i = 0; i < vertexCount; i++) {
+			for (int j = 0; j < vertexCount; j++) {
+				vertices[vertexPointer * 3] = (float) j / ((float) vertexCount - 1) * TERRAIN_SIZE;
+				float height = getHeight(j, i, image);
+				heights[j][i] = height;
+				vertices[vertexPointer * 3 + 1] = height;
+				vertices[vertexPointer * 3 + 2] = (float) i / ((float) vertexCount - 1) * TERRAIN_SIZE;
+				Vector3f normal = calculateNormal(j, i, image);
+				normals[vertexPointer * 3] = normal.x;
+				normals[vertexPointer * 3 + 1] = normal.y;
+				normals[vertexPointer * 3 + 2] = normal.z;
+				textureCoords[vertexPointer * 2] = (float) j / ((float) vertexCount - 1);
+				textureCoords[vertexPointer * 2 + 1] = (float) i / ((float) vertexCount - 1);
+				vertexPointer++;
+			}
+		}
+		int pointer = 0;
+		for (int gz = 0; gz < vertexCount - 1; gz++) {
+			for (int gx = 0; gx < vertexCount - 1; gx++) {
+				int topLeft = (gz * vertexCount) + gx;
+				int topRight = topLeft + 1;
+				int bottomLeft = ((gz + 1) * vertexCount) + gx;
+				int bottomRight = bottomLeft + 1;
+				indices[pointer++] = topLeft;
+				indices[pointer++] = bottomLeft;
+				indices[pointer++] = topRight;
+				indices[pointer++] = topRight;
+				indices[pointer++] = bottomLeft;
+				indices[pointer++] = bottomRight;
+			}
+		}
+	}
+
+	private Vector3f calculateNormal(int x, int z, HeightsGenerator generator) {
+		float heightL = getHeight(x - 1, z, generator);
+		float heightR = getHeight(x + 1, z, generator);
+		float heightD = getHeight(x, z - 1, generator);
+		float heightU = getHeight(x, z + 1, generator);
+		Vector3f normal = new Vector3f(heightL - heightR, 2f, heightD - heightU);
+		normal.normalise();
+		return normal;
+	}
+	
+	private Vector3f calculateNormal(int x, int z, BufferedImage image) {
+		float heightL = getHeight(x - 1, z, image);
+		float heightR = getHeight(x + 1, z, image);
+		float heightD = getHeight(x, z - 1, image);
+		float heightU = getHeight(x, z + 1, image);
+		Vector3f normal = new Vector3f(heightL - heightR, 2f, heightD - heightU);
+		normal.normalise();
+		return normal;
+	}
+	
+	private float getHeight(int x, int z, BufferedImage image) {
+		if (x < 0 || x >= image.getHeight() || z < 0 || z >= image.getHeight()) {
+			return 0;
+		}
+		float height = image.getRGB(x, z);
+		height += MAX_PIXEL_COLOR / 2f;
+		height /= MAX_PIXEL_COLOR / 2f;
+		height *= MAX_HEIGHT;
+		return height;
+	}
+	
+	private float getHeight(int x, int z, HeightsGenerator generator) {
+		return generator.generateHeight(x, z);
+	}
+
+	public RawModel getModel(Loader loader) {
+		return loader.loadToVAO(vertices, textureCoords, normals, indices);
+	}
+
+	public int getGridX() {
+		return gridX;
+	}
+
+	public int getGridZ() {
+		return gridZ;
+	}
+
+	public float[][] getHeights() {
+		return heights;
+	}
+}

+ 118 - 17
src/main/java/eu/tankernn/gameEngine/terrains/TerrainPack.java

@@ -1,37 +1,65 @@
 package eu.tankernn.gameEngine.terrains;
 
+import static eu.tankernn.gameEngine.settings.Settings.TERRAIN_SIZE;
+
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import org.apache.commons.lang3.tuple.Pair;
 
+import eu.tankernn.gameEngine.loader.Loader;
+import eu.tankernn.gameEngine.loader.textures.TerrainTexturePack;
+import eu.tankernn.gameEngine.loader.textures.Texture;
 import eu.tankernn.gameEngine.renderEngine.MasterRenderer;
+import eu.tankernn.gameEngine.util.IPositionable;
 
 public class TerrainPack {
-	private List<Terrain> terrains = new ArrayList<Terrain>();
-	
+	private Map<Pair<Integer, Integer>, Terrain> terrains = new HashMap<Pair<Integer, Integer>, Terrain>();
+	private List<Future<TerrainModelData>> waitingData = new ArrayList<Future<TerrainModelData>>();
+	private ExecutorService executor = Executors.newCachedThreadPool();
+	private int seed;
+	private Loader loader;
+	private TerrainTexturePack texturePack;
+	// TODO Generate blendMap based on seed.
+	private Texture blendMap;
+
+	private int lastX = 0, lastZ = 0;
+
+	public TerrainPack(Loader loader, TerrainTexturePack texturePack, Texture blendMap, int seed) {
+		this.seed = seed;
+		this.loader = loader;
+		this.texturePack = texturePack;
+		this.blendMap = blendMap;
+	}
+
 	public boolean addTerrain(Terrain terrain) {
 		if (terrain != null) {
-			terrains.add(terrain);
+			terrains.put(terrain.getGridPosition(), terrain);
 			return true;
 		} else {
 			return false;
 		}
 	}
-	
+
 	public List<Terrain> getList() {
-		return terrains;
+		return (List<Terrain>) terrains.values();
 	}
-	
+
+	public Pair<Integer, Integer> getGridPosByWorldPos(float x, float z) {
+		return Pair.of((int) Math.floor(x / TERRAIN_SIZE), (int) Math.floor(z / TERRAIN_SIZE));
+	}
+
 	public Terrain getTerrainByWorldPos(float x, float z) {
-		for (Terrain terrain: terrains) {
-			if (x >= terrain.getX() && x <= terrain.getX() + terrain.getSize()) {
-				if (z >= terrain.getZ() && z <= terrain.getZ() + terrain.getSize()) {
-					return terrain;
-				}
-			}
-		}
-		return null;
+		return terrains.get(getGridPosByWorldPos(x, z));
 	}
-	
+
 	public float getTerrainHeightByWorldPos(float x, float z) {
 		Terrain terrain = getTerrainByWorldPos(x, z);
 		if (terrain != null)
@@ -39,10 +67,83 @@ public class TerrainPack {
 		else
 			return 0;
 	}
-	
+
 	public void prepareRenderTerrains(MasterRenderer renderer) {
-		for (Terrain terrain: terrains) {
+		for (Terrain terrain : terrains.values()) {
 			renderer.processTerrain(terrain);
 		}
 	}
+
+	/**
+	 * Generates the terrains in a 3x3 area around the specified position.
+	 * 
+	 * @param pos
+	 *            The object to get the position from
+	 */
+	public void update(IPositionable pos) {
+		Pair<Integer, Integer> currentTerrain = getGridPosByWorldPos(pos.getPosition().x, pos.getPosition().z);
+
+		int newX = currentTerrain.getLeft();
+		int newZ = currentTerrain.getRight();
+
+		if (terrains.isEmpty()) {
+			terrains.put(Pair.of(newX, newZ), new Terrain(newX, newZ, loader, texturePack, blendMap, seed));
+		}
+
+		for (Future<TerrainModelData> futureData : waitingData) {
+			if (futureData.isDone()) {
+				System.out.println("Adding terrain.");
+				try {
+					TerrainModelData data = futureData.get();
+					terrains.put(Pair.of(data.getGridX(), data.getGridZ()),
+							new Terrain(data.getGridX(), data.getGridZ(), loader, texturePack, blendMap, data));
+				} catch (InterruptedException | ExecutionException e) {
+					e.printStackTrace();
+				}
+			}
+		}
+		// FIXME If a task finishes between the loop above and this statement,
+		// the terrain will never be added.
+		waitingData.removeIf(f -> f.isDone());
+
+		if (lastX != newX || lastZ != newZ) {
+			List<Pair<Integer, Integer>> toGenerate = new ArrayList<Pair<Integer, Integer>>();
+
+			for (int x = newX - 1; x <= newX + 1; x++)
+				for (int z = newZ - 1; z <= newZ + 1; z++)
+					toGenerate.add(Pair.of(x, z));
+
+			for (Pair<Integer, Integer> pair : toGenerate) {
+				if (terrains.containsKey(pair) | pair.getLeft() < 0 | pair.getRight() < 0)
+					continue;
+
+				Callable<TerrainModelData> task = new Callable<TerrainModelData>() {
+					@Override
+					public TerrainModelData call() throws Exception {
+						return new TerrainModelData(pair.getLeft(), pair.getRight(), seed);
+					}
+				};
+
+				waitingData.add(executor.submit(task));
+			}
+			
+			for (Pair<Integer, Integer> pair : terrains.keySet()) {
+				if (!toGenerate.contains(pair)) {
+					terrains.get(pair).delete();
+				}
+			}
+			
+			terrains.keySet().retainAll(toGenerate);
+			
+			System.out.println(terrains.size());
+
+			lastX = newX;
+			lastZ = newZ;
+		}
+	}
+
+	public void cleanUp() {
+		executor.shutdown();
+	}
+
 }

+ 13 - 6
src/main/java/eu/tankernn/gameEngine/util/InternalFile.java

@@ -1,11 +1,14 @@
 package eu.tankernn.gameEngine.util;
 
 import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 
 public class InternalFile {
-	
+
 	private static final String FILE_SEPARATOR = "/";
 
 	private String path;
@@ -30,7 +33,7 @@ public class InternalFile {
 		this.path = file.path + FILE_SEPARATOR + subFile;
 		this.name = subFile;
 	}
-	
+
 	public InternalFile(InternalFile file, String... subFiles) {
 		this.path = file.path;
 		for (String part : subFiles) {
@@ -43,14 +46,18 @@ public class InternalFile {
 	public String getPath() {
 		return path;
 	}
-	
+
 	@Override
-	public String toString(){
+	public String toString() {
 		return getPath();
 	}
 
-	public InputStream getInputStream() {
-		return Class.class.getResourceAsStream(path);
+	public InputStream getInputStream() throws FileNotFoundException {
+		InputStream in = Class.class.getResourceAsStream(path);
+		if (in == null) {
+			in = new FileInputStream(new File("." + path));
+		}
+		return in;
 	}
 
 	public BufferedReader getReader() throws Exception {