Преглед изворни кода

Model specification file. NM entity rendering. Files.

- Models are now specified and given an ID in a plain text file.
- Fixed normal-mapped entity rendering.
- Made the InternalFile system more reliable.
frans пре 8 година
родитељ
комит
27d49393f8
22 измењених фајлова са 297 додато и 203 уклоњено
  1. 2 0
      TODO.txt
  2. 21 44
      src/main/java/eu/tankernn/gameEngine/MainLoop.java
  3. 14 2
      src/main/java/eu/tankernn/gameEngine/TankernnGame.java
  4. 3 3
      src/main/java/eu/tankernn/gameEngine/entities/Car.java
  5. 5 24
      src/main/java/eu/tankernn/gameEngine/entities/Entity.java
  6. 3 3
      src/main/java/eu/tankernn/gameEngine/entities/Player.java
  7. 149 36
      src/main/java/eu/tankernn/gameEngine/loader/Loader.java
  8. 5 5
      src/main/java/eu/tankernn/gameEngine/loader/models/AABB.java
  9. 17 1
      src/main/java/eu/tankernn/gameEngine/loader/models/TexturedModel.java
  10. 1 1
      src/main/java/eu/tankernn/gameEngine/loader/textures/TextureUtils.java
  11. 9 8
      src/main/java/eu/tankernn/gameEngine/renderEngine/MasterRenderer.java
  12. 1 1
      src/main/java/eu/tankernn/gameEngine/renderEngine/RawModel.java
  13. 3 3
      src/main/java/eu/tankernn/gameEngine/renderEngine/entities/EntityRenderer.java
  14. 7 7
      src/main/java/eu/tankernn/gameEngine/renderEngine/entities/EntityShader.java
  15. 2 3
      src/main/java/eu/tankernn/gameEngine/renderEngine/entities/normalMap/NormalMappingRenderer.java
  16. 6 26
      src/main/java/eu/tankernn/gameEngine/renderEngine/entities/normalMap/NormalMappingShader.java
  17. 2 2
      src/main/java/eu/tankernn/gameEngine/renderEngine/entities/normalMap/normalMapFShader.glsl
  18. 4 4
      src/main/java/eu/tankernn/gameEngine/renderEngine/water/WaterFrameBuffers.java
  19. 2 1
      src/main/java/eu/tankernn/gameEngine/renderEngine/water/WaterMaster.java
  20. 3 3
      src/main/java/eu/tankernn/gameEngine/renderEngine/water/WaterRenderer.java
  21. 2 9
      src/main/java/eu/tankernn/gameEngine/terrains/TerrainPack.java
  22. 36 17
      src/main/java/eu/tankernn/gameEngine/util/InternalFile.java

+ 2 - 0
TODO.txt

@@ -1,4 +1,6 @@
 Particles and post-processing is broken.
+I am suspecting that it would be laughably easy to merge the normalmapped and non-normalmapped entity shaders.
+Texture atlases  and transparency vars with the new model spec system.
 Multiplayer
 Modular GUIs
  - Make text variables uniform/ change text rendering method

+ 21 - 44
src/main/java/eu/tankernn/gameEngine/MainLoop.java

@@ -1,6 +1,6 @@
 package eu.tankernn.gameEngine;
 
-import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Random;
@@ -21,8 +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.TexturedModel;
-import eu.tankernn.gameEngine.loader.textures.ModelTexture;
 import eu.tankernn.gameEngine.loader.textures.TerrainTexturePack;
 import eu.tankernn.gameEngine.loader.textures.Texture;
 import eu.tankernn.gameEngine.particles.ParticleMaster;
@@ -33,7 +31,6 @@ 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;
@@ -67,10 +64,10 @@ public class MainLoop {
 	static List<Entity> normalMapEntities = new ArrayList<Entity>();
 	static List<Light> lights = new ArrayList<Light>();
 
-	public static void main(String[] args) throws FileNotFoundException {
+	public static void main(String[] args) throws IOException {
 		DisplayManager.createDisplay("Tankernn Game Engine tester");
-
-		Loader loader = new Loader();
+		
+		Loader loader = new Loader(new InternalFile("models.txt"));
 
 		// ### Terrain textures ###
 
@@ -85,19 +82,11 @@ public class MainLoop {
 		TerrainPack terrainPack = new TerrainPack(loader, texturePack, blendMap, SEED);
 
 		// Player
-		RawModel playerModel = loader.loadOBJ(new InternalFile("apple.obj"));
-		TexturedModel texturedMonkeyModel = new TexturedModel(playerModel,
-				new ModelTexture(loader.loadTexture("white.png")));
-
-		ModelTexture texture = texturedMonkeyModel.getModelTexture();
-		texture.setReflectivity(3);
-		texture.setRefractivity(1.0f);
-		texture.setShineDamper(10);
-
-		Entity entity = new Entity(texturedMonkeyModel, new Vector3f(0, 0, 20), new Vector3f(0, 0, 0), 1);
+		Entity entity = new Entity(0, new Vector3f(0, 0, 20), new Vector3f(0, 0, 0), 1,
+				loader.getModel(0).getRawModel().getBoundingBox());
 		entities.add(entity);
-		TexturedModel monkey = new TexturedModel(playerModel, texture);
-		Player player = new Player(monkey, new Vector3f(10, 0, 50), new Vector3f(0, 0, 0), 1, terrainPack);
+		Player player = new Player(0, new Vector3f(10, 0, 50), new Vector3f(0, 0, 0), 1,
+				loader.getModel(0).getRawModel().getBoundingBox(), terrainPack);
 		entities.add(player);
 		Camera camera = new PlayerCamera(player, terrainPack);
 
@@ -117,13 +106,9 @@ public class MainLoop {
 		textMaster.loadText(text);
 
 		// Barrel
-		TexturedModel barrelModel = new TexturedModel(loader.loadNormalMappedOBJ(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);
 
-		Entity barrel = new Entity(barrelModel, new Vector3f(75, 10, 75), new Vector3f(0, 0, 0), 1f);
+		Entity barrel = new Entity(1, new Vector3f(75, 10, 75), new Vector3f(0, 0, 0), 1f,
+				loader.getModel(1).getRawModel().getBoundingBox());
 		normalMapEntities.add(barrel);
 
 		Light sun = new Light(new Vector3f(100000, 150000, -70000), new Vector3f(1f, 1f, 1f));
@@ -132,15 +117,7 @@ public class MainLoop {
 		lights.add(sun);
 		lights.add(flashLight);
 
-		// ### Random grass generation ###
-
-		ModelTexture textureAtlas = new ModelTexture(Texture.newTexture(new InternalFile("lantern.png")).create());
-		textureAtlas.setNumberOfRows(1);
-		TexturedModel grassModel = new TexturedModel(loader.loadOBJ(new InternalFile("lantern.obj")), textureAtlas);
-		grassModel.getModelTexture().setHasTransparency(true);
-		grassModel.getModelTexture().setShineDamper(10);
-		grassModel.getModelTexture().setReflectivity(0.5f);
-		grassModel.getModelTexture().setSpecularMap(loader.loadTexture("lanternS.png"));
+		// ### Random lantern generation ###
 
 		Random rand = new Random(SEED);
 
@@ -148,14 +125,14 @@ public class MainLoop {
 			float x = rand.nextFloat() * 1000;
 			float z = rand.nextFloat() * 1000;
 
-			entities.add(new Entity(grassModel, rand.nextInt(4),
-					new Vector3f(x, terrainPack.getTerrainHeightByWorldPos(x, z), z), new Vector3f(), 1));
+			entities.add(new Entity(2, new Vector3f(x, terrainPack.getTerrainHeightByWorldPos(x, z), z), new Vector3f(),
+					1, loader.getModel(2).getRawModel().getBoundingBox()));
 		}
 
 		terrainPack.addWaitingForTerrainHeight(entities.toArray(new Entity[entities.size()]));
 
 		// #### Water rendering ####
-		WaterMaster waterMaster = new WaterMaster(loader, DUDV_MAP, NORMAL_MAP, camera);
+		WaterMaster waterMaster = new WaterMaster(loader, loader.loadTexture(DUDV_MAP), loader.loadTexture(NORMAL_MAP), camera);
 		WaterTile water = new WaterTile(75, 75, 0);
 		waterMaster.addWaterTile(water);
 
@@ -166,7 +143,7 @@ public class MainLoop {
 		ParticleTexture particleTexture = new ParticleTexture(loader.loadTexture("particles/fire.png"), 4, false);
 		ParticleSystem ps = new ParticleSystem(particleTexture, 50, 10, 0.3f, 1);
 		particleMaster.addSystem(ps);
-		
+
 		MultisampleMultitargetFbo multisampleFbo = new MultisampleMultitargetFbo(Display.getWidth(),
 				Display.getHeight());
 		Fbo outputFbo = new Fbo(Display.getWidth(), Display.getHeight(), Fbo.DEPTH_TEXTURE);
@@ -189,7 +166,7 @@ public class MainLoop {
 				flashLight.getPosition().y = terrainPack.getTerrainHeightByWorldPos(currentPoint.x, currentPoint.z)
 						+ 1.0f;
 			}
-			
+
 			if (picker.getCurrentEntity() != null) {
 				picker.getCurrentEntity().setScale(2);
 			}
@@ -216,9 +193,9 @@ public class MainLoop {
 
 			// Sort list of lights
 			DistanceSorter.sort(lights, camera);
-			
+
 			renderer.renderShadowMap(entities, sun);
-			
+
 			ps.setPosition(player.getPosition());
 			particleMaster.update(camera);
 
@@ -227,12 +204,12 @@ public class MainLoop {
 			EnvironmentMapRenderer.renderEnvironmentMap(scene.getEnvironmentMap(), scene, player.getPosition(),
 					renderer);
 
-			//waterMaster.renderBuffers(renderer, scene);
+			waterMaster.renderBuffers(renderer, scene);
 
 			multisampleFbo.bindFrameBuffer();
 
 			renderer.renderScene(scene, new Vector4f(0, 1, 0, Float.MAX_VALUE));
-			//waterMaster.renderWater(camera, lights);
+			waterMaster.renderWater(camera, lights);
 			particleMaster.renderParticles(camera);
 
 			multisampleFbo.unbindFrameBuffer();
@@ -240,7 +217,7 @@ public class MainLoop {
 			multisampleFbo.resolveToFbo(GL30.GL_COLOR_ATTACHMENT0, outputFbo);
 			multisampleFbo.resolveToFbo(GL30.GL_COLOR_ATTACHMENT1, outputFbo2);
 			PostProcessing.doPostProcessing(outputFbo.getColourTexture(), outputFbo2.getColourTexture());
-			
+
 			guiRenderer.render(guis);
 			textMaster.render();
 

+ 14 - 2
src/main/java/eu/tankernn/gameEngine/TankernnGame.java

@@ -1,11 +1,15 @@
 package eu.tankernn.gameEngine;
 
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
 import eu.tankernn.gameEngine.entities.Camera;
 import eu.tankernn.gameEngine.loader.Loader;
 import eu.tankernn.gameEngine.renderEngine.DisplayManager;
 import eu.tankernn.gameEngine.renderEngine.MasterRenderer;
 import eu.tankernn.gameEngine.renderEngine.skybox.Skybox;
 import eu.tankernn.gameEngine.renderEngine.water.WaterMaster;
+import eu.tankernn.gameEngine.util.InternalFile;
 
 public class TankernnGame {
 	protected Loader loader;
@@ -16,10 +20,18 @@ public class TankernnGame {
 	
 	public TankernnGame(Skybox skybox, String dudvMap, String normalMap) {
 		this.sky = skybox;
-		loader = new Loader();
+		try {
+			loader = new Loader(new InternalFile("models.txt"));
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
 		camera = new Camera();
 		renderer = new MasterRenderer(loader, camera, skybox);
-		waterMaster = new WaterMaster(loader, dudvMap, normalMap, camera);
+		try {
+			waterMaster = new WaterMaster(loader, loader.loadTexture(dudvMap), loader.loadTexture(normalMap), camera);
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		}
 	}
 	
 	public void update() {

+ 3 - 3
src/main/java/eu/tankernn/gameEngine/entities/Car.java

@@ -3,7 +3,7 @@ package eu.tankernn.gameEngine.entities;
 import org.lwjgl.input.Keyboard;
 import org.lwjgl.util.vector.Vector3f;
 
-import eu.tankernn.gameEngine.loader.models.TexturedModel;
+import eu.tankernn.gameEngine.loader.models.AABB;
 import eu.tankernn.gameEngine.renderEngine.DisplayManager;
 import eu.tankernn.gameEngine.settings.Physics;
 import eu.tankernn.gameEngine.terrains.Terrain;
@@ -14,8 +14,8 @@ public class Car extends Player {
 	private static final float MAX_SPEED = 100.0f, ACCELERATION = 20.0f, DECELERATION = 10.0f, BRAKE = 40.0f,
 			TURN_FORCE = 160.0f;
 
-	public Car(TexturedModel model, Vector3f position, Vector3f rotation, float scale, TerrainPack terrainPack) {
-		super(model, position, rotation, scale, terrainPack);
+	public Car(int model, Vector3f position, Vector3f rotation, float scale, AABB boundingBox, TerrainPack terrainPack) {
+		super(model, position, rotation, scale, boundingBox, terrainPack);
 	}
 
 	@Override

+ 5 - 24
src/main/java/eu/tankernn/gameEngine/entities/Entity.java

@@ -3,41 +3,22 @@ package eu.tankernn.gameEngine.entities;
 import org.lwjgl.util.vector.Vector3f;
 
 import eu.tankernn.gameEngine.loader.models.AABB;
-import eu.tankernn.gameEngine.loader.models.TexturedModel;
 import eu.tankernn.gameEngine.util.IPositionable;
 
 public class Entity implements IPositionable {
-	private TexturedModel model;
+	private int model;
 	private Vector3f position;
 	private Vector3f rotation;
 	private float scale;
 	private AABB boundingBox;
 	
-	private int textureIndex = 0;
-	
-	public Entity(TexturedModel model, Vector3f position, Vector3f rotation, float scale) {
+	public Entity(int model, Vector3f position, Vector3f rotation, float scale, AABB boundingBox) {
 		this.model = model;
 		this.position = position;
 		this.rotation = rotation;
 		this.scale = scale;
-		this.boundingBox = model.getRawModel().getBoundingBox();
+		this.boundingBox = boundingBox;
 		this.boundingBox.updatePosition(position);
-		this.boundingBox = this.boundingBox.copy();
-	}
-	
-	public Entity(TexturedModel model, int index, Vector3f position, Vector3f rotation, float scale) {
-		this(model, position, rotation, scale);
-		this.textureIndex = index;
-	}
-	
-	public float getTextureXOffset() {
-		int column = textureIndex % model.getModelTexture().getNumberOfRows();
-		return (float) column / (float) model.getModelTexture().getNumberOfRows();
-	}
-	
-	public float getTextureYOffset() {
-		int row = textureIndex / model.getModelTexture().getNumberOfRows();
-		return (float) row / (float) model.getModelTexture().getNumberOfRows();
 	}
 	
 	public void increasePosition(float dx, float dy, float dz) {
@@ -55,11 +36,11 @@ public class Entity implements IPositionable {
 		this.boundingBox.updatePosition(this.position);
 	}
 	
-	public TexturedModel getModel() {
+	public int getModel() {
 		return model;
 	}
 	
-	public void setModel(TexturedModel model) {
+	public void setModel(int model) {
 		this.model = model;
 	}
 	

+ 3 - 3
src/main/java/eu/tankernn/gameEngine/entities/Player.java

@@ -4,7 +4,7 @@ import org.lwjgl.input.Keyboard;
 import org.lwjgl.input.Mouse;
 import org.lwjgl.util.vector.Vector3f;
 
-import eu.tankernn.gameEngine.loader.models.TexturedModel;
+import eu.tankernn.gameEngine.loader.models.AABB;
 import eu.tankernn.gameEngine.renderEngine.DisplayManager;
 import eu.tankernn.gameEngine.settings.Physics;
 import eu.tankernn.gameEngine.terrains.Terrain;
@@ -25,8 +25,8 @@ public class Player extends Entity {
 	
 	private float height = 2.0f;
 	
-	public Player(TexturedModel model, Vector3f position, Vector3f rotation, float scale, TerrainPack terrainPack) {
-		super(model, position, rotation, scale);
+	public Player(int model, Vector3f position, Vector3f rotation, float scale, AABB boundingBox, TerrainPack terrainPack) {
+		super(model, position, rotation, scale, boundingBox);
 		this.terrainPack = terrainPack;
 	}
 	

+ 149 - 36
src/main/java/eu/tankernn/gameEngine/loader/Loader.java

@@ -1,8 +1,15 @@
 package eu.tankernn.gameEngine.loader;
 
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.nio.FloatBuffer;
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
 
 import org.lwjgl.BufferUtils;
 import org.lwjgl.opengl.GL11;
@@ -11,15 +18,19 @@ import org.lwjgl.opengl.GL20;
 import org.lwjgl.opengl.GL30;
 import org.lwjgl.opengl.GL33;
 
+import eu.tankernn.gameEngine.loader.models.TexturedModel;
 import eu.tankernn.gameEngine.loader.obj.ModelData;
 import eu.tankernn.gameEngine.loader.obj.OBJFileLoader;
 import eu.tankernn.gameEngine.loader.obj.normalMapped.ModelDataNM;
 import eu.tankernn.gameEngine.loader.obj.normalMapped.NormalMappedObjLoader;
+import eu.tankernn.gameEngine.loader.textures.ModelTexture;
 import eu.tankernn.gameEngine.loader.textures.Texture;
 import eu.tankernn.gameEngine.renderEngine.RawModel;
 import eu.tankernn.gameEngine.util.InternalFile;
+
 /**
  * General purpose loader
+ * 
  * @author Frans
  *
  */
@@ -28,21 +39,27 @@ public class Loader {
 	private List<Integer> vbos = new ArrayList<Integer>();
 	private List<RawModel> rawModels = new ArrayList<RawModel>();
 	private List<Texture> textures = new ArrayList<Texture>();
-	
+	private Map<Integer, TexturedModel> models = new HashMap<Integer, TexturedModel>();
+
+	public Loader(InternalFile modelSpec) throws IOException {
+		readModelSpecification(modelSpec);
+	}
+
 	public RawModel loadToVAO(float[] vertices, float[] textureCoords, float[] normals, int[] indices) {
 		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) {
+
+	public RawModel loadToVAO(float[] vertices, float[] textureCoords, float[] normals, float[] tangents,
+			int[] indices) {
 		RawModel model = RawModel.create();
 		model.storeData(indices, vertices.length / 3, vertices, textureCoords, normals, tangents);
 		rawModels.add(model);
 		return model;
 	}
-	
+
 	public int createEmptyVBO(int floatCount) {
 		int vboID = GL15.glGenBuffers();
 		vbos.add(vboID);
@@ -51,8 +68,9 @@ public class Loader {
 		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
 		return vboID;
 	}
-	
-	public void addInstacedAttribute(int vao, int vbo, int attribute, int dataSize, int instancedDataLength, int offset) {
+
+	public void addInstacedAttribute(int vao, int vbo, int attribute, int dataSize, int instancedDataLength,
+			int offset) {
 		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
 		GL30.glBindVertexArray(vao);
 		GL20.glVertexAttribPointer(attribute, dataSize, GL11.GL_FLOAT, false, instancedDataLength * 4, offset * 4);
@@ -60,7 +78,7 @@ public class Loader {
 		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
 		GL30.glBindVertexArray(0);
 	}
-	
+
 	public void updateVBO(int vboID, float[] data, FloatBuffer buffer) {
 		buffer.clear();
 		buffer.put(data);
@@ -70,7 +88,7 @@ public class Loader {
 		GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0, buffer);
 		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
 	}
-	
+
 	public int loadToVAO(float[] positions, float[] textureCoords) {
 		int vaoID = createVAO();
 		storeDataInAttributeList(0, 2, positions);
@@ -78,77 +96,79 @@ public class Loader {
 		unbindVAO();
 		return vaoID;
 	}
-	
+
 	public RawModel loadToVAO(float[] positions, int dimensions) {
 		int vaoID = createVAO();
 		this.storeDataInAttributeList(0, dimensions, positions);
 		unbindVAO();
 		return new RawModel(vaoID, positions.length / 2);
 	}
-	
+
 	public RawModel loadToVAO(float[] positions) {
 		int vaoID = createVAO();
 		this.storeDataInAttributeList(0, 3, positions);
 		unbindVAO();
 		return new RawModel(vaoID, positions.length / 3);
 	}
-	
+
 	public RawModel loadToVAO(ModelData data) {
 		return (RawModel) loadToVAO(data.getVertices(), data.getTextureCoords(), data.getNormals(), data.getIndices());
 	}
-	
+
 	public RawModel loadToVAO(ModelDataNM data) {
-		return (RawModel) loadToVAO(data.getVertices(), data.getTextureCoords(), data.getNormals(), data.getTangents(), data.getIndices());
+		return (RawModel) loadToVAO(data.getVertices(), data.getTextureCoords(), data.getNormals(), data.getTangents(),
+				data.getIndices());
 	}
-	
+
 	/**
 	 * Loads a texture to the GPU.
-	 * @param filename The path, relative to the root of the jar file, of the file to load.
+	 * 
+	 * @param filename
+	 *            The path, relative to the root of the jar file, of the file to
+	 *            load.
 	 * @return The texture ID
+	 * @throws FileNotFoundException 
 	 */
-	public Texture loadTexture(String filename) {
+	public Texture loadTexture(String filename) throws FileNotFoundException {
 		Texture texture = Texture.newTexture(new InternalFile(filename)).create();
 		textures.add(texture);
 		return texture;
 	}
-	
+
 	/**
-	 * Creates a new cube map from the images specified.
-	 * File 0: Right face
-	 * File 1: Left face
-	 * File 2: Top face
-	 * File 3: Bottom face
-	 * File 4: Back face
-	 * File 5: Front face
+	 * Creates a new cube map from the images specified. File 0: Right face File
+	 * 1: Left face File 2: Top face File 3: Bottom face File 4: Back face File
+	 * 5: Front face
 	 * 
-	 * @param textureFiles Filenames of images that make up the cube map
+	 * @param textureFiles
+	 *            Filenames of images that make up the cube map
 	 * @return The ID of the new cube map
 	 */
-	
+
 	public Texture loadCubeMap(InternalFile[] textureFiles) {
 		Texture cubeMap = Texture.newCubeMap(textureFiles, 500);
 		textures.add(cubeMap);
 		return cubeMap;
 	}
-	
+
 	public void cleanUp() {
-		for (int vao: vaos)
+		for (int vao : vaos)
 			GL30.glDeleteVertexArrays(vao);
-		for (int vbo: vbos)
+		for (int vbo : vbos)
 			GL15.glDeleteBuffers(vbo);
-		for (Texture tex: textures)
+		for (Texture tex : textures)
 			tex.delete();
 		for (RawModel model : rawModels)
 			model.delete();
 	}
-	
+
 	private int createVAO() {
 		int vaoID = GL30.glGenVertexArrays();
 		vaos.add(vaoID);
 		GL30.glBindVertexArray(vaoID);
 		return vaoID;
 	}
-	
+
 	private void storeDataInAttributeList(int attributeNumber, int coordinateSize, float[] data) {
 		int vboID = GL15.glGenBuffers();
 		vbos.add(vboID);
@@ -158,25 +178,118 @@ public class Loader {
 		GL20.glVertexAttribPointer(attributeNumber, coordinateSize, GL11.GL_FLOAT, false, 0, 0);
 		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
 	}
-	
+
 	private void unbindVAO() {
 		GL30.glBindVertexArray(0);
 	}
-	
+
 	private FloatBuffer storeDataInFloatBuffer(float[] data) {
 		FloatBuffer buffer = BufferUtils.createFloatBuffer(data.length);
 		buffer.put(data);
 		buffer.flip();
 		return buffer;
 	}
-	
+
 	public RawModel loadOBJ(InternalFile objFile) {
 		ModelData data = OBJFileLoader.loadOBJ(objFile);
 		return this.loadToVAO(data).withBoundingBox(data);
 	}
-	
+
 	public RawModel loadNormalMappedOBJ(InternalFile objFile) {
 		ModelDataNM data = NormalMappedObjLoader.loadOBJ(objFile);
 		return this.loadToVAO(data).withBoundingBox(data);
 	}
+
+	private void readModelSpecification(InternalFile file) throws IOException {
+		Map<InternalFile, RawModel> cachedRawModels = new HashMap<InternalFile, RawModel>();
+		Map<InternalFile, Texture> cachedTextures = new HashMap<InternalFile, Texture>();
+
+		BufferedReader in = file.getReader();
+		String line;
+		String[] spec;
+
+		while (in.ready()) {
+			line = in.readLine();
+
+			if (line.startsWith("#") || line.isEmpty())
+				continue;
+
+			spec = line.split("\\s+");
+
+			int id;
+			RawModel model;
+			ModelTexture modelTexture;
+			Texture[] textures = new Texture[3];
+
+			id = Integer.parseInt(spec[0]);
+
+			InternalFile objFile = new InternalFile(spec[1]);
+			InternalFile textureFile = new InternalFile(spec[2]),
+					specularFile = spec[3].equals("null") ? null : new InternalFile(spec[3]),
+					normalFile = spec[4].equals("null") ? null : new InternalFile(spec[4]);
+
+			InternalFile[] textureFiles = { textureFile, specularFile, normalFile };
+
+			if (cachedRawModels.containsKey(objFile))
+				model = cachedRawModels.get(objFile);
+			else {
+				model = (normalFile != null ? loadNormalMappedOBJ(objFile) : loadOBJ(objFile));
+				cachedRawModels.put(objFile, model);
+			}
+
+			for (int i = 0; i < textureFiles.length; i++) {
+				if (textureFiles[i] == null)
+					textures[i] = null;
+				else if (cachedTextures.containsKey(textureFiles[i]))
+					textures[i] = cachedTextures.get(textureFiles[i]);
+				else {
+					textures[i] = this.loadTexture(textureFiles[i].getPath());
+					cachedTextures.put(textureFiles[i], textures[i]);
+				}
+			}
+
+			modelTexture = new ModelTexture(textures[0]);
+			if (textures[1] != null)
+				modelTexture.setNormalMap(textures[1]);
+			if (textures[2] != null)
+				modelTexture.setSpecularMap(textures[2]);
+
+			modelTexture.setShineDamper(Float.parseFloat(spec[5]));
+			modelTexture.setReflectivity(Float.parseFloat(spec[6]));
+			modelTexture.setRefractivity(Float.parseFloat(spec[7]));
+
+			models.put(id, new TexturedModel(model, modelTexture));
+		}
+
+		in.close();
+	}
+
+	public int registerModel(int id, TexturedModel model) throws Exception {
+		if (models.containsKey(id)) {
+			throw new Exception("There is already a model registered for the key " + id + ".");
+		} else {
+			models.put(id, model);
+			return id;
+		}
+	}
+
+	public int registerModel(TexturedModel model) throws Exception {
+		if (models.containsValue(model)) {
+			Iterator<Entry<Integer, TexturedModel>> i = models.entrySet().iterator();
+
+			while (i.hasNext()) {
+				Entry<Integer, TexturedModel> e = i.next();
+				if (e.getValue().equals(model)) {
+					return e.getKey();
+				}
+			}
+			// Should be impossible
+			throw new IllegalStateException();
+		} else
+			return registerModel(models.size(), model);
+	}
+
+	public TexturedModel getModel(int id) {
+		return models.get(id);
+	}
 }

+ 5 - 5
src/main/java/eu/tankernn/gameEngine/loader/models/AABB.java

@@ -5,7 +5,7 @@ import org.lwjgl.util.vector.Vector3f;
 import eu.tankernn.gameEngine.loader.obj.ModelData;
 
 public class AABB {
-	protected Vector3f middlePos, halfSize;
+	protected Vector3f middlePos = new Vector3f(0, 0, 0), halfSize;
 
 	public AABB(Vector3f middlePos, Vector3f halfSize) {
 		this.middlePos = middlePos;
@@ -40,10 +40,6 @@ public class AABB {
 		Vector3f fullSize = Vector3f.sub(max, min, null);
 		this.halfSize = new Vector3f(fullSize.x / 2, fullSize.y / 2, fullSize.z / 2);
 	}
-	
-	public AABB copy() {
-		return new AABB(new Vector3f(this.middlePos), new Vector3f(this.halfSize));
-	}
 
 	public void updatePosition(Vector3f pos) {
 		this.middlePos = new Vector3f(pos);
@@ -80,4 +76,8 @@ public class AABB {
 	public Vector3f getRt() {
 		return new Vector3f(middlePos.x + halfSize.x, middlePos.y + halfSize.y, middlePos.z + halfSize.z);
 	}
+	
+	public AABB copy() {
+		return new AABB(new Vector3f(this.middlePos), new Vector3f(this.halfSize));
+	}
 }

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

@@ -7,11 +7,27 @@ public class TexturedModel {
 	private RawModel rawModel;
 	private ModelTexture texture;
 	
+	private int textureIndex = 0;
+	
 	public TexturedModel(RawModel rawModel, ModelTexture texture) {
-		super();
 		this.rawModel = rawModel;
 		this.texture = texture;
 	}
+	
+	public TexturedModel(RawModel rawModel, ModelTexture texture, int textureIndex) {
+		this(rawModel, texture);
+		this.textureIndex = textureIndex;
+	}
+	
+	public float getTextureXOffset() {
+		int column = textureIndex % texture.getNumberOfRows();
+		return (float) column / (float) texture.getNumberOfRows();
+	}
+	
+	public float getTextureYOffset() {
+		int row = textureIndex / texture.getNumberOfRows();
+		return (float) row / (float) texture.getNumberOfRows();
+	}
 
 	public RawModel getRawModel() {
 		return rawModel;

+ 1 - 1
src/main/java/eu/tankernn/gameEngine/loader/textures/TextureUtils.java

@@ -67,7 +67,7 @@ public class TextureUtils {
 			in.close();
 		} catch (Exception e) {
 			e.printStackTrace();
-			System.err.println("Tried to load texture " + file.getName() + " , didn't work");
+			System.err.println("Tried to load texture " + file.getName() + ", didn't work");
 			System.exit(-1);
 		}
 		return new TextureData(buffer, width, height);

+ 9 - 8
src/main/java/eu/tankernn/gameEngine/renderEngine/MasterRenderer.java

@@ -38,6 +38,8 @@ import eu.tankernn.gameEngine.util.ICamera;
 public class MasterRenderer {
 	private static final Vector4f NO_CLIP = new Vector4f(0, 0, 0, 1);
 	
+	private Loader loader;
+	
 	private EntityRenderer<EntityShader> entityRenderer;
 	private TerrainRenderer terrainRenderer;
 	private SkyboxRenderer skyboxRenderer;
@@ -58,6 +60,7 @@ public class MasterRenderer {
 	 */
 	public MasterRenderer(Loader loader, Camera camera, Skybox skybox) {
 		enableCulling();
+		this.loader = loader;
 		terrainRenderer = new TerrainRenderer(camera.getProjectionMatrix());
 		normalMapRenderer = new NormalMappingRenderer(camera.getProjectionMatrix());
 		shadowMapRenderer = new ShadowMapMasterRenderer(camera);
@@ -98,9 +101,8 @@ public class MasterRenderer {
 		prepareScene(scene);
 		prepareBuffer();
 		entityRenderer.render(entities, shadowMapRenderer.getToShadowMapSpaceMatrix(), camera, NO_CLIP, scene.getLights(), scene.getEnvironmentMap());
+		normalMapRenderer.render(normalMapEntities, NO_CLIP, scene.getLights(), camera);
 		terrainRenderer.render(terrains, shadowMapRenderer.getToShadowMapSpaceMatrix(), camera, NO_CLIP, scene.getLights());
-		
-		
 	}
 
 	/**
@@ -115,7 +117,6 @@ public class MasterRenderer {
 	 */
 	public void render(List<Light> lights, ICamera camera, Vector4f clipPlane, Texture environmentMap) {
 		prepareBuffer();
-
 		entityRenderer.render(entities, shadowMapRenderer.getToShadowMapSpaceMatrix(), camera, clipPlane, lights, environmentMap);
 		normalMapRenderer.render(normalMapEntities, clipPlane, lights, camera);
 		terrainRenderer.render(terrains, shadowMapRenderer.getToShadowMapSpaceMatrix(), camera, clipPlane, lights);
@@ -126,7 +127,9 @@ public class MasterRenderer {
 		entities.clear();
 		terrains.clear();
 		normalMapEntities.clear();
-		scene.getTerrainPack().prepareRenderTerrains(this);
+		for (Terrain t : scene.getTerrainPack().getTerrains()) {
+			processTerrain(t);
+		}
 		for (Entity e : scene.getEntities()) {
 			processEntity(e);
 		}
@@ -142,7 +145,7 @@ public class MasterRenderer {
 	 *            Entity to add to the list
 	 */
 	public void processEntity(Entity entity) {
-		TexturedModel entityModel = entity.getModel();
+		TexturedModel entityModel = loader.getModel(entity.getModel());
 		List<Entity> batch = entities.get(entityModel);
 		if (batch != null) {
 			batch.add(entity);
@@ -160,7 +163,7 @@ public class MasterRenderer {
 	 *            Entity to add to the list
 	 */
 	public void processNormalMappedEntity(Entity entity) {
-		TexturedModel entityModel = entity.getModel();
+		TexturedModel entityModel = loader.getModel(entity.getModel());
 		List<Entity> batch = normalMapEntities.get(entityModel);
 		if (batch != null) {
 			batch.add(entity);
@@ -218,6 +221,4 @@ public class MasterRenderer {
 																	// framebuffer
 		getShadowMapTexture().bindToUnit(5);
 	}
-	
-	
 }

+ 1 - 1
src/main/java/eu/tankernn/gameEngine/renderEngine/RawModel.java

@@ -145,6 +145,6 @@ public class RawModel {
 	}
 	
 	public AABB getBoundingBox() {
-		return this.boundingBox;
+		return this.boundingBox.copy();
 	}
 }

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

@@ -74,7 +74,7 @@ public class EntityRenderer<S extends EntityShader> {
 			prepareTexturedModel(model, environmentMap);
 			List<Entity> batch = entities.get(model);
 			for (Entity entity : batch) {
-				prepareInstance(entity);
+				prepareInstance(entity, model);
 				GL11.glDrawElements(GL11.GL_TRIANGLES, model.getRawModel().getIndexCount(), GL11.GL_UNSIGNED_INT, 0);
 			}
 			unbindTexturedModel(model);
@@ -113,11 +113,11 @@ public class EntityRenderer<S extends EntityShader> {
 		model.getRawModel().unbind(0, 1, 2);
 	}
 
-	protected void prepareInstance(Entity entity) {
+	protected void prepareInstance(Entity entity, TexturedModel model) {
 		Vector3f rot = entity.getRotation();
 		Matrix4f transformationMatrix = Maths.createTransformationMatrix(entity.getPosition(), rot.x, rot.y, rot.z,
 				entity.getScale());
 		shader.transformationMatrix.loadMatrix(transformationMatrix);
-		shader.offset.loadVec2(entity.getTextureXOffset(), entity.getTextureYOffset());
+		shader.offset.loadVec2(model.getTextureXOffset(), model.getTextureYOffset());
 	}
 }

+ 7 - 7
src/main/java/eu/tankernn/gameEngine/renderEngine/entities/EntityShader.java

@@ -17,20 +17,20 @@ public class EntityShader extends ShaderProgram {
 
 	protected UniformMatrix transformationMatrix = new UniformMatrix("transformationMatrix");
 	protected UniformMatrix projectionMatrix = new UniformMatrix("projectionMatrix");
-	protected UniformViewMatrix viewMatrix = new UniformViewMatrix("viewMatrix");
+	public UniformViewMatrix viewMatrix = new UniformViewMatrix("viewMatrix");
 
-	protected UniformFloat shineDamper = new UniformFloat("shineDamper");
-	protected UniformFloat reflectivity = new UniformFloat("reflectivity");
+	public UniformFloat shineDamper = new UniformFloat("shineDamper");
+	public UniformFloat reflectivity = new UniformFloat("reflectivity");
 	protected UniformFloat refractivity = new UniformFloat("refractivity");
 	protected UniformBoolean useFakeLighting = new UniformBoolean("useFakeLighting");
-	protected UniformVec3 skyColor = new UniformVec3("skyColor");
-	protected UniformFloat numberOfRows = new UniformFloat("numberOfRows");
+	public UniformVec3 skyColor = new UniformVec3("skyColor");
+	public UniformFloat numberOfRows = new UniformFloat("numberOfRows");
 	protected UniformVec2 offset = new UniformVec2("offset");
-	protected UniformVec4 plane = new UniformVec4("plane");
+	public UniformVec4 plane = new UniformVec4("plane");
 	protected UniformMatrix toShadowMapSpace = new UniformMatrix("toShadowMapSpace");
 	protected UniformSampler shadowMap = new UniformSampler("shadowMap");
 	protected UniformSampler specularMap = new UniformSampler("specularMap");
-	protected UniformBoolean usesSpecularMap = new UniformBoolean("usesSpecularMap");
+	public UniformBoolean usesSpecularMap = new UniformBoolean("usesSpecularMap");
 	protected UniformSampler modelTexture = new UniformSampler("modelTexture");
 	protected UniformVec3 cameraPosition = new UniformVec3("cameraPosition");
 	protected UniformSampler enviroMap = new UniformSampler("enviroMap");

+ 2 - 3
src/main/java/eu/tankernn/gameEngine/renderEngine/entities/normalMap/NormalMappingRenderer.java

@@ -31,7 +31,7 @@ public class NormalMappingRenderer extends EntityRenderer<NormalMappingShader> {
 			prepareTexturedModel(model);
 			List<Entity> batch = entities.get(model);
 			for (Entity entity : batch) {
-				prepareInstance(entity);
+				prepareInstance(entity, model);
 				GL11.glDrawElements(GL11.GL_TRIANGLES, model.getRawModel().getIndexCount(), GL11.GL_UNSIGNED_INT, 0);
 			}
 			unbindTexturedModel(model);
@@ -64,8 +64,7 @@ public class NormalMappingRenderer extends EntityRenderer<NormalMappingShader> {
 
 	private void prepare(Vector4f clipPlane, List<Light> lights, ICamera camera) {
 		shader.plane.loadVec4(clipPlane);
-		// need to be public variables in MasterRenderer
-		shader.skyColour.loadVec3(Settings.RED, Settings.GREEN, Settings.BLUE);
+		shader.skyColor.loadVec3(Settings.RED, Settings.GREEN, Settings.BLUE);
 		Matrix4f viewMatrix = camera.getViewMatrix();
 
 		shader.loadLights(lights, viewMatrix);

+ 6 - 26
src/main/java/eu/tankernn/gameEngine/renderEngine/entities/normalMap/NormalMappingShader.java

@@ -8,44 +8,24 @@ import org.lwjgl.util.vector.Vector4f;
 
 import eu.tankernn.gameEngine.entities.Light;
 import eu.tankernn.gameEngine.renderEngine.entities.EntityShader;
-import eu.tankernn.gameEngine.renderEngine.shaders.UniformBoolean;
-import eu.tankernn.gameEngine.renderEngine.shaders.UniformFloat;
-import eu.tankernn.gameEngine.renderEngine.shaders.UniformMatrix;
 import eu.tankernn.gameEngine.renderEngine.shaders.UniformSampler;
-import eu.tankernn.gameEngine.renderEngine.shaders.UniformVec2;
 import eu.tankernn.gameEngine.renderEngine.shaders.UniformVec3;
-import eu.tankernn.gameEngine.renderEngine.shaders.UniformVec4;
-import eu.tankernn.gameEngine.renderEngine.shaders.UniformViewMatrix;
 
 public class NormalMappingShader extends EntityShader {
 
 	private static final String VERTEX_FILE = "/eu/tankernn/gameEngine/renderEngine/entities/normalMap/normalMapVShader.glsl";
 	private static final String FRAGMENT_FILE = "/eu/tankernn/gameEngine/renderEngine/entities/normalMap/normalMapFShader.glsl";
-
-	protected UniformMatrix transformationMatrix = new UniformMatrix("transformationMatrix");
-	protected UniformMatrix projectionMatrix = new UniformMatrix("projectionMatrix");
-	protected UniformViewMatrix viewMatrix = new UniformViewMatrix("viewMatrix");
+	
 	private UniformVec3[] lightPositionEyeSpace;
-	protected UniformFloat shineDamper = new UniformFloat("shineDamper");
-	protected UniformFloat reflectivity = new UniformFloat("reflectivity");
-	protected UniformVec3 skyColour = new UniformVec3("skyColour");
-	protected UniformFloat numberOfRows = new UniformFloat("numberOfRows");
-	protected UniformVec2 offset = new UniformVec2("offset");
-	protected UniformVec4 plane = new UniformVec4("plane");
-	protected UniformSampler modelTexture = new UniformSampler("modelTexture");
 	protected UniformSampler normalMap = new UniformSampler("normalMap");
-	protected UniformSampler specularMap = new UniformSampler("specularMap");
-	protected UniformBoolean usesSpecularMap = new UniformBoolean("usesSpecularMap");
 
 	public NormalMappingShader() {
 		super(VERTEX_FILE, FRAGMENT_FILE, "position", "textureCoordinates", "normal", "tangent");
 		this.getLightUniformLocations();
-		super.storeAllUniformLocations(transformationMatrix, projectionMatrix, viewMatrix, viewMatrix, shineDamper,
-				reflectivity, skyColour, numberOfRows, offset, plane, modelTexture, normalMap, specularMap,
-				usesSpecularMap);
-		super.storeAllUniformLocations(lightPositionEyeSpace);
+		super.storeAllUniformLocations(transformationMatrix, projectionMatrix, viewMatrix, shineDamper, reflectivity,
+				skyColor, numberOfRows, offset, plane, modelTexture, normalMap, specularMap, usesSpecularMap);
 	}
-	
+
 	@Override
 	protected void getLightUniformLocations() {
 		lightPositionEyeSpace = new UniformVec3[MAX_LIGHTS];
@@ -57,7 +37,7 @@ public class NormalMappingShader extends EntityShader {
 			attenuation[i] = new UniformVec3("attenuation[" + i + "]");
 		}
 	}
-	
+
 	public void loadLights(List<Light> lights, Matrix4f viewMatrix) {
 		for (int i = 0; i < MAX_LIGHTS; i++) {
 			if (i < lights.size()) {
@@ -71,7 +51,7 @@ public class NormalMappingShader extends EntityShader {
 			}
 		}
 	}
-	
+
 	@Override
 	public void loadLights(List<Light> lights) {
 		throw new NullPointerException("Use loadLights(List<Light> lights, Matrix4f viewMatrix) instead.");

+ 2 - 2
src/main/java/eu/tankernn/gameEngine/renderEngine/entities/normalMap/normalMapFShader.glsl

@@ -16,7 +16,7 @@ uniform vec3 lightColor[4];
 uniform vec3 attenuation[4];
 uniform float shineDamper;
 uniform float reflectivity;
-uniform vec3 skyColour;
+uniform vec3 skyColor;
 
 void main(void){
 	
@@ -58,7 +58,7 @@ void main(void){
 	}
 
 	out_Color =  vec4(totalDiffuse,1.0) * textureColour + vec4(totalSpecular,1.0);
-	out_Color = mix(vec4(skyColour,1.0),out_Color, visibility);
+	out_Color = mix(vec4(skyColor,1.0),out_Color, visibility);
 	
 	out_BrightColor = vec4(0.0);
 }

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

@@ -9,11 +9,11 @@ import eu.tankernn.gameEngine.loader.textures.TextureUtils;
 
 public class WaterFrameBuffers {
 	
-	protected static final int REFLECTION_WIDTH = 1920;
-	private static final int REFLECTION_HEIGHT = 1080;
+	protected static final int REFLECTION_WIDTH = 1024 * 1;
+	private static final int REFLECTION_HEIGHT = 1024 * 1;
 	
-	protected static final int REFRACTION_WIDTH = 360;
-	private static final int REFRACTION_HEIGHT = 180;
+	protected static final int REFRACTION_WIDTH = 1024 / 2;
+	private static final int REFRACTION_HEIGHT = 1024 / 2;
 	
 	private int reflectionFrameBuffer;
 	private Texture reflectionTexture;

+ 2 - 1
src/main/java/eu/tankernn/gameEngine/renderEngine/water/WaterMaster.java

@@ -10,6 +10,7 @@ import org.lwjgl.util.vector.Vector4f;
 import eu.tankernn.gameEngine.entities.Camera;
 import eu.tankernn.gameEngine.entities.Light;
 import eu.tankernn.gameEngine.loader.Loader;
+import eu.tankernn.gameEngine.loader.textures.Texture;
 import eu.tankernn.gameEngine.renderEngine.MasterRenderer;
 import eu.tankernn.gameEngine.renderEngine.Scene;
 import eu.tankernn.gameEngine.util.ICamera;
@@ -20,7 +21,7 @@ public class WaterMaster {
 	private List<WaterTile> waterTiles = new ArrayList<WaterTile>();
 	private WaterRenderer waterRenderer;
 	
-	public WaterMaster(Loader loader, String dudvTexture, String normalMap, ICamera camera) {
+	public WaterMaster(Loader loader, Texture dudvTexture, Texture normalMap, ICamera camera) {
 		waterRenderer = new WaterRenderer(loader, dudvTexture, normalMap, waterShader, camera.getProjectionMatrix(), buffers);
 	}
 	

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

@@ -33,11 +33,11 @@ public class WaterRenderer {
 	private Texture dudvTexture;
 	private Texture normalMap;
 
-	public WaterRenderer(Loader loader, String dudvTexture, String normalMap, WaterShader shader, Matrix4f projectionMatrix, WaterFrameBuffers buffers) {
+	public WaterRenderer(Loader loader, Texture dudvTexture, Texture normalMap, WaterShader shader, Matrix4f projectionMatrix, WaterFrameBuffers buffers) {
 		this.shader = shader;
 		this.buffers = buffers;
-		this.dudvTexture = loader.loadTexture(dudvTexture);
-		this.normalMap = loader.loadTexture(normalMap);
+		this.dudvTexture = dudvTexture;
+		this.normalMap = normalMap;
 		shader.start();
 		shader.connectTextureUnits();
 		shader.projectionMatrix.loadMatrix(projectionMatrix);

+ 2 - 9
src/main/java/eu/tankernn/gameEngine/terrains/TerrainPack.java

@@ -18,7 +18,6 @@ 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 {
@@ -50,8 +49,8 @@ public class TerrainPack {
 		}
 	}
 
-	public List<Terrain> getList() {
-		return (List<Terrain>) terrains.values();
+	public Terrain[] getTerrains() {
+		return terrains.values().toArray(new Terrain[terrains.size()]);
 	}
 
 	public Pair<Integer, Integer> getGridPosByWorldPos(float x, float z) {
@@ -70,12 +69,6 @@ public class TerrainPack {
 			return 0;
 	}
 
-	public void prepareRenderTerrains(MasterRenderer renderer) {
-		for (Terrain terrain : terrains.values()) {
-			renderer.processTerrain(terrain);
-		}
-	}
-
 	/**
 	 * Generates the terrains in a 3x3 area around the specified position.
 	 * 

+ 36 - 17
src/main/java/eu/tankernn/gameEngine/util/InternalFile.java

@@ -16,24 +16,22 @@ public class InternalFile {
 	private String path;
 	private String name;
 
-	public InternalFile(String path) {
-		this.path = FILE_SEPARATOR + path;
-		String[] dirs = path.split(FILE_SEPARATOR);
-		this.name = dirs[dirs.length - 1];
-	}
+	public InternalFile(String path) throws FileNotFoundException {
+		this.path = path;
+		if (!path.startsWith("/") || path.isEmpty())
+			this.path = FILE_SEPARATOR + path;
 
-	public InternalFile(String... paths) {
-		this.path = "";
-		for (String part : paths) {
-			this.path += (FILE_SEPARATOR + part);
-		}
 		String[] dirs = path.split(FILE_SEPARATOR);
 		this.name = dirs[dirs.length - 1];
+		try {
+			getInputStream().close();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
 	}
 
-	public InternalFile(InternalFile file, String subFile) {
-		this.path = file.path + FILE_SEPARATOR + subFile;
-		this.name = subFile;
+	public InternalFile(String... paths) throws FileNotFoundException {
+		this(String.join(FILE_SEPARATOR, paths));
 	}
 
 	public InternalFile(InternalFile file, String... subFiles) {
@@ -54,11 +52,16 @@ public class InternalFile {
 		return getPath();
 	}
 
+	@SuppressWarnings("resource")
 	public InputStream getInputStream() throws FileNotFoundException {
-		InputStream in = Class.class.getResourceAsStream(path);
-		if (in == null) {
+		InputStream in = null;
+		try {
 			in = new FileInputStream(new File("." + path));
+		} catch (FileNotFoundException ex) {
+			in = InternalFile.class.getResourceAsStream(path);
 		}
+		if (in == null)
+			throw new FileNotFoundException("Cannot find file " + path + " on classpath.");
 		return in;
 	}
 
@@ -76,9 +79,25 @@ public class InternalFile {
 	public String getName() {
 		return name;
 	}
-	
+
 	public static InternalFile[] fromFilenames(String dir, String[] filenames, String extension) {
-		return Arrays.asList(filenames).stream().map(f -> new InternalFile(dir + FILE_SEPARATOR + f + "." + extension)).toArray(size -> new InternalFile[size]);
+		return Arrays.asList(filenames).stream().map(f -> {
+			try {
+				return new InternalFile(dir + FILE_SEPARATOR + f + "." + extension);
+			} catch (FileNotFoundException e) {
+				e.printStackTrace();
+				return null;
+			}
+		}).toArray(size -> new InternalFile[size]);
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (!obj.getClass().equals(this.getClass())) {
+			return false;
+		} else {
+			return this.getPath().equals(((InternalFile) obj).getPath());
+		}
 	}
 
 }