瀏覽代碼

Imported all of the necessary code from TheThinMatrix's repo

Tankernn 8 年之前
父節點
當前提交
8ef30adee1
共有 36 個文件被更改,包括 2612 次插入3 次删除
  1. 150 0
      src/main/java/eu/tankernn/gameEngine/animation/animatedModel/AnimatedModel.java
  2. 143 0
      src/main/java/eu/tankernn/gameEngine/animation/animatedModel/Joint.java
  3. 48 0
      src/main/java/eu/tankernn/gameEngine/animation/animation/Animation.java
  4. 228 0
      src/main/java/eu/tankernn/gameEngine/animation/animation/Animator.java
  5. 117 0
      src/main/java/eu/tankernn/gameEngine/animation/animation/JointTransform.java
  6. 56 0
      src/main/java/eu/tankernn/gameEngine/animation/animation/KeyFrame.java
  7. 212 0
      src/main/java/eu/tankernn/gameEngine/animation/animation/Quaternion.java
  8. 85 0
      src/main/java/eu/tankernn/gameEngine/animation/loaders/AnimatedModelLoader.java
  9. 77 0
      src/main/java/eu/tankernn/gameEngine/animation/loaders/AnimationLoader.java
  10. 87 0
      src/main/java/eu/tankernn/gameEngine/animation/renderer/AnimatedModelRenderer.java
  11. 44 0
      src/main/java/eu/tankernn/gameEngine/animation/renderer/AnimatedModelShader.java
  12. 20 0
      src/main/java/eu/tankernn/gameEngine/animation/renderer/animatedEntityFragment.glsl
  13. 36 0
      src/main/java/eu/tankernn/gameEngine/animation/renderer/animatedEntityVertex.glsl
  14. 21 0
      src/main/java/eu/tankernn/gameEngine/loader/colladaLoader/AnimatedModelData.java
  15. 13 0
      src/main/java/eu/tankernn/gameEngine/loader/colladaLoader/AnimationData.java
  16. 101 0
      src/main/java/eu/tankernn/gameEngine/loader/colladaLoader/AnimationLoader.java
  17. 33 0
      src/main/java/eu/tankernn/gameEngine/loader/colladaLoader/ColladaLoader.java
  18. 206 0
      src/main/java/eu/tankernn/gameEngine/loader/colladaLoader/GeometryLoader.java
  19. 27 0
      src/main/java/eu/tankernn/gameEngine/loader/colladaLoader/JointData.java
  20. 14 0
      src/main/java/eu/tankernn/gameEngine/loader/colladaLoader/JointTransformData.java
  21. 13 0
      src/main/java/eu/tankernn/gameEngine/loader/colladaLoader/JointsData.java
  22. 68 0
      src/main/java/eu/tankernn/gameEngine/loader/colladaLoader/JointsLoader.java
  23. 19 0
      src/main/java/eu/tankernn/gameEngine/loader/colladaLoader/KeyFrameData.java
  24. 58 0
      src/main/java/eu/tankernn/gameEngine/loader/colladaLoader/MeshData.java
  25. 77 0
      src/main/java/eu/tankernn/gameEngine/loader/colladaLoader/SkinLoader.java
  26. 16 0
      src/main/java/eu/tankernn/gameEngine/loader/colladaLoader/SkinningData.java
  27. 97 0
      src/main/java/eu/tankernn/gameEngine/loader/colladaLoader/Vertex.java
  28. 65 0
      src/main/java/eu/tankernn/gameEngine/loader/colladaLoader/VertexSkinData.java
  29. 168 0
      src/main/java/eu/tankernn/gameEngine/loader/xmlLoader/XmlNode.java
  30. 102 0
      src/main/java/eu/tankernn/gameEngine/loader/xmlLoader/XmlParser.java
  31. 1 1
      src/main/java/eu/tankernn/gameEngine/postProcessing/bloom/BrightFilter.java
  32. 1 1
      src/main/java/eu/tankernn/gameEngine/postProcessing/gaussianBlur/HorizontalBlur.java
  33. 1 1
      src/main/java/eu/tankernn/gameEngine/postProcessing/gaussianBlur/VerticalBlur.java
  34. 88 0
      src/main/java/eu/tankernn/gameEngine/renderEngine/Vao.java
  35. 32 0
      src/main/java/eu/tankernn/gameEngine/renderEngine/shaders/UniformMat4Array.java
  36. 88 0
      src/main/java/eu/tankernn/gameEngine/util/OpenGlUtils.java

+ 150 - 0
src/main/java/eu/tankernn/gameEngine/animation/animatedModel/AnimatedModel.java

@@ -0,0 +1,150 @@
+package eu.tankernn.gameEngine.animation.animatedModel;
+
+import org.lwjgl.util.vector.Matrix4f;
+
+import eu.tankernn.gameEngine.animation.animation.Animation;
+import eu.tankernn.gameEngine.animation.animation.Animator;
+import eu.tankernn.gameEngine.loader.textures.Texture;
+import eu.tankernn.gameEngine.renderEngine.Vao;
+
+/**
+ * 
+ * This class represents an entity in the world that can be animated. It
+ * contains the model's VAO which contains the mesh data, the texture, and the
+ * root joint of the joint hierarchy, or "skeleton". It also holds an int which
+ * represents the number of joints that the model's skeleton contains, and has
+ * its own {@link Animator} instance which can be used to apply animations to
+ * this entity.
+ * 
+ * @author Karl
+ *
+ */
+public class AnimatedModel {
+
+	// skin
+	private final Vao model;
+	private final Texture texture;
+
+	// skeleton
+	private final Joint rootJoint;
+	private final int jointCount;
+
+	private final Animator animator;
+
+	/**
+	 * Creates a new entity capable of animation. The inverse bind transform for
+	 * all joints is calculated in this constructor. The bind transform is
+	 * simply the original (no pose applied) transform of a joint in relation to
+	 * the model's origin (model-space). The inverse bind transform is simply
+	 * that but inverted.
+	 * 
+	 * @param model
+	 *            - the VAO containing the mesh data for this entity. This
+	 *            includes vertex positions, normals, texture coords, IDs of
+	 *            joints that affect each vertex, and their corresponding
+	 *            weights.
+	 * @param texture
+	 *            - the diffuse texture for the entity.
+	 * @param rootJoint
+	 *            - the root joint of the joint hierarchy which makes up the
+	 *            "skeleton" of the entity.
+	 * @param jointCount
+	 *            - the number of joints in the joint hierarchy (skeleton) for
+	 *            this entity.
+	 * 
+	 */
+	public AnimatedModel(Vao model, Texture texture, Joint rootJoint, int jointCount) {
+		this.model = model;
+		this.texture = texture;
+		this.rootJoint = rootJoint;
+		this.jointCount = jointCount;
+		this.animator = new Animator(this);
+		rootJoint.calcInverseBindTransform(new Matrix4f());
+	}
+
+	/**
+	 * @return The VAO containing all the mesh data for this entity.
+	 */
+	public Vao getModel() {
+		return model;
+	}
+
+	/**
+	 * @return The diffuse texture for this entity.
+	 */
+	public Texture getTexture() {
+		return texture;
+	}
+
+	/**
+	 * @return The root joint of the joint hierarchy. This joint has no parent,
+	 *         and every other joint in the skeleton is a descendant of this
+	 *         joint.
+	 */
+	public Joint getRootJoint() {
+		return rootJoint;
+	}
+
+	/**
+	 * Deletes the OpenGL objects associated with this entity, namely the model
+	 * (VAO) and texture.
+	 */
+	public void delete() {
+		model.delete();
+		texture.delete();
+	}
+
+	/**
+	 * Instructs this entity to carry out a given animation. To do this it
+	 * basically sets the chosen animation as the current animation in the
+	 * {@link Animator} object.
+	 * 
+	 * @param animation
+	 *            - the animation to be carried out.
+	 */
+	public void doAnimation(Animation animation) {
+		animator.doAnimation(animation);
+	}
+
+	/**
+	 * Updates the animator for this entity, basically updating the animated
+	 * pose of the entity. Must be called every frame.
+	 */
+	public void update() {
+		animator.update();
+	}
+
+	/**
+	 * Gets an array of the all important model-space transforms of all the
+	 * joints (with the current animation pose applied) in the entity. The
+	 * joints are ordered in the array based on their joint index. The position
+	 * of each joint's transform in the array is equal to the joint's index.
+	 * 
+	 * @return The array of model-space transforms of the joints in the current
+	 *         animation pose.
+	 */
+	public Matrix4f[] getJointTransforms() {
+		Matrix4f[] jointMatrices = new Matrix4f[jointCount];
+		addJointsToArray(rootJoint, jointMatrices);
+		return jointMatrices;
+	}
+
+	/**
+	 * This adds the current model-space transform of a joint (and all of its
+	 * descendants) into an array of transforms. The joint's transform is added
+	 * into the array at the position equal to the joint's index.
+	 * 
+	 * @param headJoint
+	 *            - the current joint being added to the array. This method also
+	 *            adds the transforms of all the descendents of this joint too.
+	 * @param jointMatrices
+	 *            - the array of joint transforms that is being filled.
+	 */
+	private void addJointsToArray(Joint headJoint, Matrix4f[] jointMatrices) {
+		jointMatrices[headJoint.index] = headJoint.getAnimatedTransform();
+		for (Joint childJoint : headJoint.children) {
+			addJointsToArray(childJoint, jointMatrices);
+		}
+	}
+
+}

+ 143 - 0
src/main/java/eu/tankernn/gameEngine/animation/animatedModel/Joint.java

@@ -0,0 +1,143 @@
+package eu.tankernn.gameEngine.animation.animatedModel;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.lwjgl.util.vector.Matrix4f;
+
+import eu.tankernn.gameEngine.animation.animation.Animator;
+
+/**
+ * 
+ * Represents a joint in a "skeleton". It contains the index of the joint which
+ * determines where in the vertex shader uniform array the joint matrix for this
+ * joint is loaded up to. It also contains the name of the bone, and a list of
+ * all the child joints.
+ * 
+ * The "animatedTransform" matrix is the joint transform that I keep referring
+ * to in the tutorial. This is the transform that gets loaded up to the vertex
+ * shader and is used to transform vertices. It is a model-space transform that
+ * transforms the joint from it's bind (original position, no animation applied)
+ * position to it's current position in the current pose. Changing this
+ * transform changes the position/rotation of the joint in the animated entity.
+ * 
+ * The two other matrices are transforms that are required to calculate the
+ * "animatedTransform" in the {@link Animator} class. It also has the local bind
+ * transform which is the original (no pose/animation applied) transform of the
+ * joint relative to the parent joint (in bone-space).
+ * 
+ * The "localBindTransform" is the original (bind) transform of the joint
+ * relative to its parent (in bone-space). The inverseBindTransform is that bind
+ * transform in model-space, but inversed.
+ * 
+ * @author Karl
+ *
+ */
+public class Joint {
+
+	public final int index;// ID
+	public final String name;
+	public final List<Joint> children = new ArrayList<Joint>();
+
+	private Matrix4f animatedTransform = new Matrix4f();
+	
+	private final Matrix4f localBindTransform;
+	private Matrix4f inverseBindTransform = new Matrix4f();
+
+	/**
+	 * @param index
+	 *            - the joint's index (ID).
+	 * @param name
+	 *            - the name of the joint. This is how the joint is named in the
+	 *            collada file, and so is used to identify which joint a joint
+	 *            transform in an animation keyframe refers to.
+	 * @param bindLocalTransform
+	 *            - the bone-space transform of the joint in the bind position.
+	 */
+	public Joint(int index, String name, Matrix4f bindLocalTransform) {
+		this.index = index;
+		this.name = name;
+		this.localBindTransform = bindLocalTransform;
+	}
+
+	/**
+	 * Adds a child joint to this joint. Used during the creation of the joint
+	 * hierarchy. Joints can have multiple children, which is why they are
+	 * stored in a list (e.g. a "hand" joint may have multiple "finger" children
+	 * joints).
+	 * 
+	 * @param child
+	 *            - the new child joint of this joint.
+	 */
+	public void addChild(Joint child) {
+		this.children.add(child);
+	}
+
+	/**
+	 * The animated transform is the transform that gets loaded up to the shader
+	 * and is used to deform the vertices of the "skin". It represents the
+	 * transformation from the joint's bind position (original position in
+	 * model-space) to the joint's desired animation pose (also in model-space).
+	 * This matrix is calculated by taking the desired model-space transform of
+	 * the joint and multiplying it by the inverse of the starting model-space
+	 * transform of the joint.
+	 * 
+	 * @return The transformation matrix of the joint which is used to deform
+	 *         associated vertices of the skin in the shaders.
+	 */
+	public Matrix4f getAnimatedTransform() {
+		return animatedTransform;
+	}
+
+	/**
+	 * This method allows those all important "joint transforms" (as I referred
+	 * to them in the tutorial) to be set by the animator. This is used to put
+	 * the joints of the animated model in a certain pose.
+	 * 
+	 * @param animationTransform - the new joint transform.
+	 */
+	public void setAnimationTransform(Matrix4f animationTransform) {
+		this.animatedTransform = animationTransform;
+	}
+
+	/**
+	 * This returns the inverted model-space bind transform. The bind transform
+	 * is the original model-space transform of the joint (when no animation is
+	 * applied). This returns the inverse of that, which is used to calculate
+	 * the animated transform matrix which gets used to transform vertices in
+	 * the shader.
+	 * 
+	 * @return The inverse of the joint's bind transform (in model-space).
+	 */
+	public Matrix4f getInverseBindTransform() {
+		return inverseBindTransform;
+	}
+
+	/**
+	 * This is called during set-up, after the joints hierarchy has been
+	 * created. This calculates the model-space bind transform of this joint
+	 * like so: </br>
+	 * </br>
+	 * {@code bindTransform = parentBindTransform * localBindTransform}</br>
+	 * </br>
+	 * where "bindTransform" is the model-space bind transform of this joint,
+	 * "parentBindTransform" is the model-space bind transform of the parent
+	 * joint, and "localBindTransform" is the bone-space bind transform of this
+	 * joint. It then calculates and stores the inverse of this model-space bind
+	 * transform, for use when calculating the final animation transform each
+	 * frame. It then recursively calls the method for all of the children
+	 * joints, so that they too calculate and store their inverse bind-pose
+	 * transform.
+	 * 
+	 * @param parentBindTransform
+	 *            - the model-space bind transform of the parent joint.
+	 */
+	protected void calcInverseBindTransform(Matrix4f parentBindTransform) {
+		Matrix4f bindTransform = Matrix4f.mul(parentBindTransform, localBindTransform, null);
+		Matrix4f.invert(bindTransform, inverseBindTransform);
+		for (Joint child : children) {
+			child.calcInverseBindTransform(bindTransform);
+		}
+	}
+
+}

+ 48 - 0
src/main/java/eu/tankernn/gameEngine/animation/animation/Animation.java

@@ -0,0 +1,48 @@
+package eu.tankernn.gameEngine.animation.animation;
+
+import eu.tankernn.gameEngine.animation.animatedModel.AnimatedModel;
+
+/**
+ * 
+ * * Represents an animation that can applied to an {@link AnimatedModel} . It
+ * contains the length of the animation in seconds, and a list of
+ * {@link KeyFrame}s.
+ * 
+ * @author Karl
+ * 
+ *
+ */
+public class Animation {
+
+	private final float length;
+	private final KeyFrame[] keyFrames;
+
+	/**
+	 * @param lengthInSeconds
+	 *            - the total length of the animation in seconds.
+	 * @param frames
+	 *            - all the keyframes for the animation, ordered by time of
+	 *            appearance in the animation.
+	 */
+	public Animation(float lengthInSeconds, KeyFrame[] frames) {
+		this.keyFrames = frames;
+		this.length = lengthInSeconds;
+	}
+
+	/**
+	 * @return The length of the animation in seconds.
+	 */
+	public float getLength() {
+		return length;
+	}
+
+	/**
+	 * @return An array of the animation's keyframes. The array is ordered based
+	 *         on the order of the keyframes in the animation (first keyframe of
+	 *         the animation in array position 0).
+	 */
+	public KeyFrame[] getKeyFrames() {
+		return keyFrames;
+	}
+
+}

+ 228 - 0
src/main/java/eu/tankernn/gameEngine/animation/animation/Animator.java

@@ -0,0 +1,228 @@
+package eu.tankernn.gameEngine.animation.animation;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.lwjgl.util.vector.Matrix4f;
+
+import eu.tankernn.gameEngine.animation.animatedModel.AnimatedModel;
+import eu.tankernn.gameEngine.animation.animatedModel.Joint;
+import eu.tankernn.gameEngine.renderEngine.DisplayManager;
+
+
+/**
+ * 
+ * This class contains all the functionality to apply an animation to an
+ * animated entity. An Animator instance is associated with just one
+ * {@link AnimatedModel}. It also keeps track of the running time (in seconds)
+ * of the current animation, along with a reference to the currently playing
+ * animation for the corresponding entity.
+ * 
+ * An Animator instance needs to be updated every frame, in order for it to keep
+ * updating the animation pose of the associated entity. The currently playing
+ * animation can be changed at any time using the doAnimation() method. The
+ * Animator will keep looping the current animation until a new animation is
+ * chosen.
+ * 
+ * The Animator calculates the desired current animation pose by interpolating
+ * between the previous and next keyframes of the animation (based on the
+ * current animation time). The Animator then updates the transforms all of the
+ * joints each frame to match the current desired animation pose.
+ * 
+ * @author Karl
+ *
+ */
+public class Animator {
+
+	private final AnimatedModel entity;
+
+	private float animationTime = 0;
+	private Animation currentAnimation;
+
+	/**
+	 * @param entity
+	 *            - the entity which will by animated by this animator.
+	 */
+	public Animator(AnimatedModel entity) {
+		this.entity = entity;
+	}
+
+	/**
+	 * Indicates that the entity should carry out the given animation. Resets
+	 * the animation time so that the new animation starts from the beginning.
+	 * 
+	 * @param animation
+	 *            - the new animation to carry out.
+	 */
+	public void doAnimation(Animation animation) {
+		this.animationTime = 0;
+		this.currentAnimation = animation;
+	}
+
+	/**
+	 * This method should be called each frame to update the animation currently
+	 * being played. This increases the animation time (and loops it back to
+	 * zero if necessary), finds the pose that the entity should be in at that
+	 * time of the animation, and then applies that pose to all the model's
+	 * joints by setting the joint transforms.
+	 */
+	public void update() {
+		if (currentAnimation == null) {
+			return;
+		}
+		increaseAnimationTime();
+		Map<String, Matrix4f> currentPose = calculateCurrentAnimationPose();
+		applyPoseToJoints(currentPose, entity.getRootJoint(), new Matrix4f());
+
+	}
+
+	/**
+	 * Increases the current animation time which allows the animation to
+	 * progress. If the current animation has reached the end then the timer is
+	 * reset, causing the animation to loop.
+	 */
+	private void increaseAnimationTime() {
+		animationTime += DisplayManager.getFrameTimeSeconds();
+		if (animationTime > currentAnimation.getLength()) {
+			this.animationTime %= currentAnimation.getLength();
+		}
+	}
+
+	/**
+	 * This method returns the current animation pose of the entity. It returns
+	 * the desired local-space transforms for all the joints in a map, indexed
+	 * by the name of the joint that they correspond to.
+	 * 
+	 * The pose is calculated based on the previous and next keyframes in the
+	 * current animation. Each keyframe provides the desired pose at a certain
+	 * time in the animation, so the animated pose for the current time can be
+	 * calculated by interpolating between the previous and next keyframe.
+	 * 
+	 * This method first finds the preious and next keyframe, calculates how far
+	 * between the two the current animation is, and then calculated the pose
+	 * for the current animation time by interpolating between the transforms at
+	 * those keyframes.
+	 * 
+	 * @return The current pose as a map of the desired local-space transforms
+	 *         for all the joints. The transforms are indexed by the name ID of
+	 *         the joint that they should be applied to.
+	 */
+	private Map<String, Matrix4f> calculateCurrentAnimationPose() {
+		KeyFrame[] frames = getPreviousAndNextFrames();
+		float progression = calculateProgression(frames[0], frames[1]);
+		return interpolatePoses(frames[0], frames[1], progression);
+	}
+
+	/**
+	 * This is the method where the animator calculates and sets those all-
+	 * important "joint transforms" that I talked about so much in the tutorial.
+	 * 
+	 * This method applies the current pose to a given joint, and all of its
+	 * descendants. It does this by getting the desired local-transform for the
+	 * current joint, before applying it to the joint. Before applying the
+	 * transformations it needs to be converted from local-space to model-space
+	 * (so that they are relative to the model's origin, rather than relative to
+	 * the parent joint). This can be done by multiplying the local-transform of
+	 * the joint with the model-space transform of the parent joint.
+	 * 
+	 * The same thing is then done to all the child joints.
+	 * 
+	 * Finally the inverse of the joint's bind transform is multiplied with the
+	 * model-space transform of the joint. This basically "subtracts" the
+	 * joint's original bind (no animation applied) transform from the desired
+	 * pose transform. The result of this is then the transform required to move
+	 * the joint from its original model-space transform to it's desired
+	 * model-space posed transform. This is the transform that needs to be
+	 * loaded up to the vertex shader and used to transform the vertices into
+	 * the current pose.
+	 * 
+	 * @param currentPose
+	 *            - a map of the local-space transforms for all the joints for
+	 *            the desired pose. The map is indexed by the name of the joint
+	 *            which the transform corresponds to.
+	 * @param joint
+	 *            - the current joint which the pose should be applied to.
+	 * @param parentTransform
+	 *            - the desired model-space transform of the parent joint for
+	 *            the pose.
+	 */
+	private void applyPoseToJoints(Map<String, Matrix4f> currentPose, Joint joint, Matrix4f parentTransform) {
+		Matrix4f currentLocalTransform = currentPose.get(joint.name);
+		Matrix4f currentTransform = Matrix4f.mul(parentTransform, currentLocalTransform, null);
+		for (Joint childJoint : joint.children) {
+			applyPoseToJoints(currentPose, childJoint, currentTransform);
+		}
+		Matrix4f.mul(currentTransform, joint.getInverseBindTransform(), currentTransform);
+		joint.setAnimationTransform(currentTransform);
+	}
+
+	/**
+	 * Finds the previous keyframe in the animation and the next keyframe in the
+	 * animation, and returns them in an array of length 2. If there is no
+	 * previous frame (perhaps current animation time is 0.5 and the first
+	 * keyframe is at time 1.5) then the next keyframe is used as both the
+	 * previous and next keyframe. The reverse happens if there is no next
+	 * keyframe.
+	 * 
+	 * @return The previous and next keyframes, in an array which therefore will
+	 *         always have a length of 2.
+	 */
+	private KeyFrame[] getPreviousAndNextFrames() {
+		KeyFrame previousFrame = null;
+		KeyFrame nextFrame = null;
+		for (KeyFrame frame : currentAnimation.getKeyFrames()) {
+			if (frame.getTimeStamp() > animationTime) {
+				nextFrame = frame;
+				break;
+			}
+			previousFrame = frame;
+		}
+		previousFrame = previousFrame == null ? nextFrame : previousFrame;
+		nextFrame = nextFrame == null ? previousFrame : nextFrame;
+		return new KeyFrame[] { previousFrame, nextFrame };
+	}
+
+	/**
+	 * Calculates how far between the previous and next keyframe the current
+	 * animation time is, and returns it as a value between 0 and 1.
+	 * 
+	 * @param previousFrame
+	 *            - the previous keyframe in the animation.
+	 * @param nextFrame
+	 *            - the next keyframe in the animation.
+	 * @return A number between 0 and 1 indicating how far between the two
+	 *         keyframes the current animation time is.
+	 */
+	private float calculateProgression(KeyFrame previousFrame, KeyFrame nextFrame) {
+		float timeDifference = nextFrame.getTimeStamp() - previousFrame.getTimeStamp();
+		return (animationTime - previousFrame.getTimeStamp()) / timeDifference;
+	}
+
+	/**
+	 * Calculates all the local-space joint transforms for the desired current
+	 * pose by interpolating between the transforms at the previous and next
+	 * keyframes.
+	 * 
+	 * @param previousFrame
+	 *            - the previous keyframe in the animation.
+	 * @param nextFrame
+	 *            - the next keyframe in the animation.
+	 * @param progression
+	 *            - a number between 0 and 1 indicating how far between the
+	 *            previous and next keyframes the current animation time is.
+	 * @return The local-space transforms for all the joints for the desired
+	 *         current pose. They are returned in a map, indexed by the name of
+	 *         the joint to which they should be applied.
+	 */
+	private Map<String, Matrix4f> interpolatePoses(KeyFrame previousFrame, KeyFrame nextFrame, float progression) {
+		Map<String, Matrix4f> currentPose = new HashMap<String, Matrix4f>();
+		for (String jointName : previousFrame.getJointKeyFrames().keySet()) {
+			JointTransform previousTransform = previousFrame.getJointKeyFrames().get(jointName);
+			JointTransform nextTransform = nextFrame.getJointKeyFrames().get(jointName);
+			JointTransform currentTransform = JointTransform.interpolate(previousTransform, nextTransform, progression);
+			currentPose.put(jointName, currentTransform.getLocalTransform());
+		}
+		return currentPose;
+	}
+
+}

+ 117 - 0
src/main/java/eu/tankernn/gameEngine/animation/animation/JointTransform.java

@@ -0,0 +1,117 @@
+package eu.tankernn.gameEngine.animation.animation;
+
+import org.lwjgl.util.vector.Matrix4f;
+import org.lwjgl.util.vector.Vector3f;
+
+/**
+ * 
+ * Represents the local bone-space transform of a joint at a certain keyframe
+ * during an animation. This includes the position and rotation of the joint,
+ * relative to the parent joint (for the root joint it's relative to the model's
+ * origin, seeing as the root joint has no parent). The transform is stored as a
+ * position vector and a quaternion (rotation) so that these values can be
+ * easily interpolated, a functionality that this class also provides.
+ * 
+ * @author Karl
+ *
+ */
+
+public class JointTransform {
+
+	// remember, this position and rotation are relative to the parent bone!
+	private final Vector3f position;
+	private final Quaternion rotation;
+
+	/**
+	 * 
+	 * @param position
+	 *            - the position of the joint relative to the parent joint
+	 *            (bone-space) at a certain keyframe. For example, if this joint
+	 *            is at (5, 12, 0) in the model's coordinate system, and the
+	 *            parent of this joint is at (2, 8, 0), then the position of
+	 *            this joint relative to the parent is (3, 4, 0).
+	 * @param rotation
+	 *            - the rotation of the joint relative to the parent joint
+	 *            (bone-space) at a certain keyframe.
+	 */
+	public JointTransform(Vector3f position, Quaternion rotation) {
+		this.position = position;
+		this.rotation = rotation;
+	}
+
+	/**
+	 * @param localTransform
+	 *            - the joint's local-transform at a certain keyframe of an
+	 *            animation.
+	 */
+	public JointTransform(Matrix4f localTransform) {
+		this.position = new Vector3f(localTransform.m30, localTransform.m31, localTransform.m32);
+		this.rotation = new Quaternion(localTransform);
+	}
+
+	/**
+	 * In this method the bone-space transform matrix is constructed by
+	 * translating an identity matrix using the position variable and then
+	 * applying the rotation. The rotation is applied by first converting the
+	 * quaternion into a rotation matrix, which is then multiplied with the
+	 * transform matrix.
+	 * 
+	 * @return This bone-space joint transform as a matrix. The exact same
+	 *         transform as represented by the position and rotation in this
+	 *         instance, just in matrix form.
+	 */
+	protected Matrix4f getLocalTransform() {
+		Matrix4f matrix = new Matrix4f();
+		matrix.translate(position);
+		Matrix4f.mul(matrix, rotation.toRotationMatrix(), matrix);
+		return matrix;
+	}
+
+	/**
+	 * Interpolates between two transforms based on the progression value. The
+	 * result is a new transform which is part way between the two original
+	 * transforms. The translation can simply be linearly interpolated, but the
+	 * rotation interpolation is slightly more complex, using a method called
+	 * "SLERP" to spherically-linearly interpolate between 2 quaternions
+	 * (rotations). This gives a much much better result than trying to linearly
+	 * interpolate between Euler rotations.
+	 * 
+	 * @param frameA
+	 *            - the previous transform
+	 * @param frameB
+	 *            - the next transform
+	 * @param progression
+	 *            - a number between 0 and 1 indicating how far between the two
+	 *            transforms to interpolate. A progression value of 0 would
+	 *            return a transform equal to "frameA", a value of 1 would
+	 *            return a transform equal to "frameB". Everything else gives a
+	 *            transform somewhere in-between the two.
+	 * @return
+	 */
+	protected static JointTransform interpolate(JointTransform frameA, JointTransform frameB, float progression) {
+		Vector3f pos = interpolate(frameA.position, frameB.position, progression);
+		Quaternion rot = Quaternion.nlerp(frameA.rotation, frameB.rotation, progression);
+		return new JointTransform(pos, rot);
+	}
+
+	/**
+	 * Linearly interpolates between two translations based on a "progression"
+	 * value.
+	 * 
+	 * @param start
+	 *            - the start translation.
+	 * @param end
+	 *            - the end translation.
+	 * @param progression
+	 *            - a value between 0 and 1 indicating how far to interpolate
+	 *            between the two translations.
+	 * @return
+	 */
+	private static Vector3f interpolate(Vector3f start, Vector3f end, float progression) {
+		float x = start.x + (end.x - start.x) * progression;
+		float y = start.y + (end.y - start.y) * progression;
+		float z = start.z + (end.z - start.z) * progression;
+		return new Vector3f(x, y, z);
+	}
+
+}

+ 56 - 0
src/main/java/eu/tankernn/gameEngine/animation/animation/KeyFrame.java

@@ -0,0 +1,56 @@
+package eu.tankernn.gameEngine.animation.animation;
+
+import java.util.Map;
+
+/**
+ * 
+ * Represents one keyframe of an animation. This contains the timestamp of the
+ * keyframe, which is the time (in seconds) from the start of the animation when
+ * this keyframe occurs.
+ * 
+ * It also contains the desired bone-space transforms of all of the joints in
+ * the animated entity at this keyframe in the animation (i.e. it contains all
+ * the joint transforms for the "pose" at this time of the animation.). The
+ * joint transforms are stored in a map, indexed by the name of the joint that
+ * they should be applied to.
+ * 
+ * @author Karl
+ *
+ */
+public class KeyFrame {
+
+	private final float timeStamp;
+	private final Map<String, JointTransform> pose;
+
+	/**
+	 * @param timeStamp
+	 *            - the time (in seconds) that this keyframe occurs during the
+	 *            animation.
+	 * @param jointKeyFrames
+	 *            - the local-space transforms for all the joints at this
+	 *            keyframe, indexed by the name of the joint that they should be
+	 *            applied to.
+	 */
+	public KeyFrame(float timeStamp, Map<String, JointTransform> jointKeyFrames) {
+		this.timeStamp = timeStamp;
+		this.pose = jointKeyFrames;
+	}
+
+	/**
+	 * @return The time in seconds of the keyframe in the animation.
+	 */
+	protected float getTimeStamp() {
+		return timeStamp;
+	}
+
+	/**
+	 * @return The desired bone-space transforms of all the joints at this
+	 *         keyframe, of the animation, indexed by the name of the joint that
+	 *         they correspond to. This basically represents the "pose" at this
+	 *         keyframe.
+	 */
+	protected Map<String, JointTransform> getJointKeyFrames() {
+		return pose;
+	}
+
+}

+ 212 - 0
src/main/java/eu/tankernn/gameEngine/animation/animation/Quaternion.java

@@ -0,0 +1,212 @@
+package eu.tankernn.gameEngine.animation.animation;
+
+import org.lwjgl.util.vector.Matrix4f;
+
+/**
+ * A quaternion simply represents a 3D rotation. The maths behind it is quite
+ * complex (literally; it involves complex numbers) so I wont go into it in too
+ * much detail. The important things to note are that it represents a 3d
+ * rotation, it's very easy to interpolate between two quaternion rotations
+ * (which would not be easy to do correctly with Euler rotations or rotation
+ * matrices), and you can convert to and from matrices fairly easily. So when we
+ * need to interpolate between rotations we'll represent them as quaternions,
+ * but when we need to apply the rotations to anything we'll convert back to a
+ * matrix.
+ * 
+ * @author Karl
+ *
+ */
+public class Quaternion {
+
+	private float x, y, z, w;
+
+	/**
+	 * Creates a quaternion and normalizes it.
+	 * 
+	 * @param x
+	 * @param y
+	 * @param z
+	 * @param w
+	 */
+	public Quaternion(float x, float y, float z, float w) {
+		this.x = x;
+		this.y = y;
+		this.z = z;
+		this.w = w;
+		normalize();
+	}
+
+	/**
+	 * Extracts the rotation part of a transformation matrix and converts it to
+	 * a quaternion using the magic of maths.
+	 * 
+	 * @param matrix
+	 *            - the transformation matrix containing the rotation which this
+	 *            quaternion shall represent.
+	 */
+	public Quaternion(Matrix4f matrix) {
+		float diagonal = matrix.m00 + matrix.m11 + matrix.m22;
+		if (diagonal > 0) {
+			float w4 = (float) (Math.sqrt(diagonal + 1f) * 2f);
+			this.w = w4 / 4f;
+			this.x = (matrix.m21 - matrix.m12) / w4;
+			this.y = (matrix.m02 - matrix.m20) / w4;
+			this.z = (matrix.m10 - matrix.m01) / w4;
+		} else if ((matrix.m00 > matrix.m11) && (matrix.m00 > matrix.m22)) {
+			float x4 = (float) (Math.sqrt(1f + matrix.m00 - matrix.m11 - matrix.m22) * 2f);
+			this.w = (matrix.m21 - matrix.m12) / x4;
+			this.x = x4 / 4f;
+			this.y = (matrix.m01 + matrix.m10) / x4;
+			this.z = (matrix.m02 + matrix.m20) / x4;
+		} else if (matrix.m11 > matrix.m22) {
+			float y4 = (float) (Math.sqrt(1f + matrix.m11 - matrix.m00 - matrix.m22) * 2f);
+			this.w = (matrix.m02 - matrix.m20) / y4;
+			this.x = (matrix.m01 + matrix.m10) / y4;
+			this.y = y4 / 4f;
+			this.z = (matrix.m12 + matrix.m21) / y4;
+		} else {
+			float z4 = (float) (Math.sqrt(1f + matrix.m22 - matrix.m00 - matrix.m11) * 2f);
+			this.w = (matrix.m10 - matrix.m01) / z4;
+			this.x = (matrix.m02 + matrix.m20) / z4;
+			this.y = (matrix.m12 + matrix.m21) / z4;
+			this.z = z4 / 4f;
+		}
+		this.normalize();
+	}
+	
+//	public static Quaternion nlerp(Quaternion a, Quaternion b, float blend){
+//		a.normalize();
+//		b.normalize();
+//		float w = a.w + (blend * (b.w - a.w));
+//		float x = a.x + (blend * (b.x - a.x));
+//		float y = a.y + (blend * (b.y - a.y));
+//		float z = a.z + (blend * (b.z - a.z));
+//		Quaternion q = new Quaternion(x, y, z, w);
+//		q.normalize();
+//		return q;
+//	}
+	
+	public static Quaternion nlerp(Quaternion a, Quaternion b, float blend)
+	{
+		Quaternion result = new Quaternion(0, 0, 0, 0);
+	     float dot = a.w*b.w + a.x*b.x + a.y*b.y + a.z*b.z;
+	     float blendI = 1.0f - blend;
+	     if(dot < 0.0f)
+	     {
+	          float tmpFw = -b.w;
+	          float tmpFx = -b.x;
+	          float tmpFy = -b.y;
+	          float tmpFz = -b.z;
+	          result.w = blendI*a.w + blend*tmpFw;
+	          result.x = blendI*a.x + blend*tmpFx;
+	          result.y = blendI*a.y + blend*tmpFy;
+	          result.z = blendI*a.z + blend*tmpFz;
+	     }
+	     else
+	     {
+	          result.w = blendI*a.w + blend*b.w;
+	          result.x = blendI*a.x + blend*b.x;
+	          result.y = blendI*a.y + blend*b.y;
+	          result.z = blendI*a.z + blend*b.z;
+	     }
+	     result.normalize();
+	     return result;
+	}
+
+	/**
+	 * Converts the quaternion to a 4x4 matrix representing the exact same
+	 * rotation as this quaternion. (The rotation is only contained in the
+	 * top-left 3x3 part, but a 4x4 matrix is returned here for convenience
+	 * seeing as it will be multiplied with other 4x4 matrices).
+	 * 
+	 * @return The rotation matrix which represents the exact same rotation as
+	 *         this quaternion.
+	 */
+	public Matrix4f toRotationMatrix() {
+		Matrix4f matrix = new Matrix4f();
+		final float xy = x * y;
+		final float xz = x * z;
+		final float xw = x * w;
+		final float yz = y * z;
+		final float yw = y * w;
+		final float zw = z * w;
+		final float xSquared = x * x;
+		final float ySquared = y * y;
+		final float zSquared = z * z;
+		matrix.m00 = 1 - 2 * (ySquared + zSquared);
+		matrix.m01 = 2 * (xy - zw);
+		matrix.m02 = 2 * (xz + yw);
+		matrix.m03 = 0;
+		matrix.m10 = 2 * (xy + zw);
+		matrix.m11 = 1 - 2 * (xSquared + zSquared);
+		matrix.m12 = 2 * (yz - xw);
+		matrix.m13 = 0;
+		matrix.m20 = 2 * (xz - yw);
+		matrix.m21 = 2 * (yz + xw);
+		matrix.m22 = 1 - 2 * (xSquared + ySquared);
+		matrix.m23 = 0;
+		matrix.m30 = 0;
+		matrix.m31 = 0;
+		matrix.m32 = 0;
+		matrix.m33 = 1;
+		return matrix;
+	}
+
+	/**
+	 * Normalizes the quaternion.
+	 */
+	public void normalize() {
+		float mag = (float) Math.sqrt(w * w + x * x + y * y + z * z);
+		w /= mag;
+		x /= mag;
+		y /= mag;
+		z /= mag;
+	}
+
+	/**
+	 * Interpolates between two quaternion rotations and returns a new
+	 * quaternion which represents a rotation somewhere in between the two input
+	 * rotations.
+	 * 
+	 * @param start
+	 *            - the starting rotation.
+	 * @param end
+	 *            - the end rotation.
+	 * @param progression
+	 *            - a value between 0 and 1 indicating how much to interpolate
+	 *            between the two rotations. 0 would retrun the start rotation,
+	 *            and 1 would return the end rotation.
+	 * @return The interpolated rotation as a quaternion.
+	 */
+	public static Quaternion slerp(Quaternion start, Quaternion end, float progression) {
+		start.normalize();
+		end.normalize();
+		final float d = start.x * end.x + start.y * end.y + start.z * end.z + start.w * end.w;
+		float absDot = d < 0f ? -d : d;
+		float scale0 = 1f - progression;
+		float scale1 = progression;
+
+		if ((1 - absDot) > 0.1f) {
+
+			final float angle = (float) Math.acos(absDot);
+			final float invSinTheta = 1f / (float) Math.sin(angle);
+			scale0 = ((float) Math.sin((1f - progression) * angle) * invSinTheta);
+			scale1 = ((float) Math.sin((progression * angle)) * invSinTheta);
+		}
+
+		if (d < 0f) {
+			scale1 = -scale1;
+		}
+		float newX = (scale0 * start.x) + (scale1 * end.x);
+		float newY = (scale0 * start.y) + (scale1 * end.y);
+		float newZ = (scale0 * start.z) + (scale1 * end.z);
+		float newW = (scale0 * start.w) + (scale1 * end.w);
+		return new Quaternion(newX, newY, newZ, newW);
+	}
+
+	@Override
+	public String toString() {
+		return x + ", " + y + ", " + z + ", " + w;
+	}
+
+}

+ 85 - 0
src/main/java/eu/tankernn/gameEngine/animation/loaders/AnimatedModelLoader.java

@@ -0,0 +1,85 @@
+package eu.tankernn.gameEngine.animation.loaders;
+
+import eu.tankernn.gameEngine.animation.animatedModel.AnimatedModel;
+import eu.tankernn.gameEngine.animation.animatedModel.Joint;
+import eu.tankernn.gameEngine.loader.colladaLoader.AnimatedModelData;
+import eu.tankernn.gameEngine.loader.colladaLoader.ColladaLoader;
+import eu.tankernn.gameEngine.loader.colladaLoader.JointData;
+import eu.tankernn.gameEngine.loader.colladaLoader.JointsData;
+import eu.tankernn.gameEngine.loader.colladaLoader.MeshData;
+import eu.tankernn.gameEngine.loader.textures.Texture;
+import eu.tankernn.gameEngine.renderEngine.Vao;
+import eu.tankernn.gameEngine.util.InternalFile;
+
+public class AnimatedModelLoader {
+	
+	public static final int MAX_WEIGHTS = 3;
+
+	/**
+	 * Creates an AnimatedEntity from the data in an entity file. It loads up
+	 * the collada model data, stores the extracted data in a VAO, sets up the
+	 * joint heirarchy, and loads up the entity's texture.
+	 * 
+	 * @param entityFile
+	 *            - the file containing the data for the entity.
+	 * @return The animated entity (no animation applied though)
+	 */
+	public static AnimatedModel loadEntity(InternalFile modelFile, InternalFile textureFile) {
+		AnimatedModelData entityData = ColladaLoader.loadColladaModel(modelFile, MAX_WEIGHTS);
+		Vao model = createVao(entityData.getMeshData());
+		Texture texture = loadTexture(textureFile);
+		JointsData skeletonData = entityData.getJointsData();
+		Joint headJoint = createJoints(skeletonData.headJoint);
+		return new AnimatedModel(model, texture, headJoint, skeletonData.jointCount);
+	}
+
+	/**
+	 * Loads up the diffuse texture for the model.
+	 * 
+	 * @param textureFile
+	 *            - the texture file.
+	 * @return The diffuse texture.
+	 */
+	private static Texture loadTexture(InternalFile textureFile) {
+		Texture diffuseTexture = Texture.newTexture(textureFile).anisotropic().create();
+		return diffuseTexture;
+	}
+
+	/**
+	 * Constructs the joint-hierarchy skeleton from the data extracted from the
+	 * collada file.
+	 * 
+	 * @param data
+	 *            - the joints data from the collada file for the head joint.
+	 * @return The created joint, with all its descendants added.
+	 */
+	private static Joint createJoints(JointData data) {
+		Joint joint = new Joint(data.index, data.nameId, data.bindLocalTransform);
+		for (JointData child : data.children) {
+			joint.addChild(createJoints(child));
+		}
+		return joint;
+	}
+
+	/**
+	 * Stores the mesh data in a VAO.
+	 * 
+	 * @param data
+	 *            - all the data about the mesh that needs to be stored in the
+	 *            VAO.
+	 * @return The VAO containing all the mesh data for the model.
+	 */
+	private static Vao createVao(MeshData data) {
+		Vao vao = Vao.create();
+		vao.bind();
+		vao.createIndexBuffer(data.getIndices());
+		vao.createAttribute(0, data.getVertices(), 3);
+		vao.createAttribute(1, data.getTextureCoords(), 2);
+		vao.createAttribute(2, data.getNormals(), 3);
+		vao.createIntAttribute(3, data.getJointIds(), 3);
+		vao.createAttribute(4, data.getVertexWeights(), 3);
+		vao.unbind();
+		return vao;
+	}
+
+}

+ 77 - 0
src/main/java/eu/tankernn/gameEngine/animation/loaders/AnimationLoader.java

@@ -0,0 +1,77 @@
+package eu.tankernn.gameEngine.animation.loaders;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.lwjgl.util.vector.Matrix4f;
+import org.lwjgl.util.vector.Vector3f;
+
+import eu.tankernn.gameEngine.animation.animation.Animation;
+import eu.tankernn.gameEngine.animation.animation.JointTransform;
+import eu.tankernn.gameEngine.animation.animation.KeyFrame;
+import eu.tankernn.gameEngine.animation.animation.Quaternion;
+import eu.tankernn.gameEngine.loader.colladaLoader.AnimationData;
+import eu.tankernn.gameEngine.loader.colladaLoader.ColladaLoader;
+import eu.tankernn.gameEngine.loader.colladaLoader.JointTransformData;
+import eu.tankernn.gameEngine.loader.colladaLoader.KeyFrameData;
+import eu.tankernn.gameEngine.util.InternalFile;
+
+/**
+ * This class loads up an animation collada file, gets the information from it,
+ * and then creates and returns an {@link Animation} from the extracted data.
+ * 
+ * @author Karl
+ *
+ */
+public class AnimationLoader {
+
+	/**
+	 * Loads up a collada animation file, and returns and animation created from
+	 * the extracted animation data from the file.
+	 * 
+	 * @param colladaFile
+	 *            - the collada file containing data about the desired
+	 *            animation.
+	 * @return The animation made from the data in the file.
+	 */
+	public static Animation loadAnimation(InternalFile colladaFile) {
+		AnimationData animationData = ColladaLoader.loadColladaAnimation(colladaFile);
+		KeyFrame[] frames = new KeyFrame[animationData.keyFrames.length];
+		for (int i = 0; i < frames.length; i++) {
+			frames[i] = createKeyFrame(animationData.keyFrames[i]);
+		}
+		return new Animation(animationData.lengthSeconds, frames);
+	}
+
+	/**
+	 * Creates a keyframe from the data extracted from the collada file.
+	 * 
+	 * @param data
+	 *            - the data about the keyframe that was extracted from the
+	 *            collada file.
+	 * @return The keyframe.
+	 */
+	private static KeyFrame createKeyFrame(KeyFrameData data) {
+		Map<String, JointTransform> map = new HashMap<String, JointTransform>();
+		for (JointTransformData jointData : data.jointTransforms) {
+			JointTransform jointTransform = createTransform(jointData);
+			map.put(jointData.jointNameId, jointTransform);
+		}
+		return new KeyFrame(data.time, map);
+	}
+
+	/**
+	 * Creates a joint transform from the data extracted from the collada file.
+	 * 
+	 * @param data
+	 *            - the data from the collada file.
+	 * @return The joint transform.
+	 */
+	private static JointTransform createTransform(JointTransformData data) {
+		Matrix4f mat = data.jointLocalTransform;
+		Vector3f translation = new Vector3f(mat.m30, mat.m31, mat.m32);
+		Quaternion rotation = new Quaternion(mat);
+		return new JointTransform(translation, rotation);
+	}
+
+}

+ 87 - 0
src/main/java/eu/tankernn/gameEngine/animation/renderer/AnimatedModelRenderer.java

@@ -0,0 +1,87 @@
+package eu.tankernn.gameEngine.animation.renderer;
+
+import org.lwjgl.opengl.GL11;
+import org.lwjgl.util.vector.Vector3f;
+
+import eu.tankernn.gameEngine.animation.animatedModel.AnimatedModel;
+import eu.tankernn.gameEngine.util.ICamera;
+import eu.tankernn.gameEngine.util.OpenGlUtils;
+
+
+/**
+ * 
+ * This class deals with rendering an animated entity. Nothing particularly new
+ * here. The only exciting part is that the joint transforms get loaded up to
+ * the shader in a uniform array.
+ * 
+ * @author Karl
+ *
+ */
+public class AnimatedModelRenderer {
+
+	private AnimatedModelShader shader;
+
+	/**
+	 * Initializes the shader program used for rendering animated models.
+	 */
+	public AnimatedModelRenderer() {
+		this.shader = new AnimatedModelShader();
+	}
+
+	/**
+	 * Renders an animated entity. The main thing to note here is that all the
+	 * joint transforms are loaded up to the shader to a uniform array. Also 5
+	 * attributes of the VAO are enabled before rendering, to include joint
+	 * indices and weights.
+	 * 
+	 * @param entity
+	 *            - the animated entity to be rendered.
+	 * @param camera
+	 *            - the camera used to render the entity.
+	 * @param lightDir
+	 *            - the direction of the light in the scene.
+	 */
+	public void render(AnimatedModel entity, ICamera camera, Vector3f lightDir) {
+		prepare(camera, lightDir);
+		entity.getTexture().bindToUnit(0);
+		entity.getModel().bind(0, 1, 2, 3, 4);
+		shader.jointTransforms.loadMatrixArray(entity.getJointTransforms());
+		GL11.glDrawElements(GL11.GL_TRIANGLES, entity.getModel().getIndexCount(), GL11.GL_UNSIGNED_INT, 0);
+		entity.getModel().unbind(0, 1, 2, 3, 4);
+		finish();
+	}
+
+	/**
+	 * Deletes the shader program when the game closes.
+	 */
+	public void cleanUp() {
+		shader.cleanUp();
+	}
+
+	/**
+	 * Starts the shader program and loads up the projection view matrix, as
+	 * well as the light direction. Enables and disables a few settings which
+	 * should be pretty self-explanatory.
+	 * 
+	 * @param camera
+	 *            - the camera being used.
+	 * @param lightDir
+	 *            - the direction of the light in the scene.
+	 */
+	private void prepare(ICamera camera, Vector3f lightDir) {
+		shader.start();
+		shader.projectionViewMatrix.loadMatrix(camera.getProjectionViewMatrix());
+		shader.lightDirection.loadVec3(lightDir);
+		OpenGlUtils.antialias(true);
+		OpenGlUtils.disableBlending();
+		OpenGlUtils.enableDepthTesting(true);
+	}
+
+	/**
+	 * Stops the shader program after rendering the entity.
+	 */
+	private void finish() {
+		shader.stop();
+	}
+
+}

+ 44 - 0
src/main/java/eu/tankernn/gameEngine/animation/renderer/AnimatedModelShader.java

@@ -0,0 +1,44 @@
+package eu.tankernn.gameEngine.animation.renderer;
+
+import eu.tankernn.gameEngine.renderEngine.shaders.ShaderProgram;
+import eu.tankernn.gameEngine.renderEngine.shaders.UniformMat4Array;
+import eu.tankernn.gameEngine.renderEngine.shaders.UniformMatrix;
+import eu.tankernn.gameEngine.renderEngine.shaders.UniformSampler;
+import eu.tankernn.gameEngine.renderEngine.shaders.UniformVec3;
+
+public class AnimatedModelShader extends ShaderProgram {
+
+	private static final int MAX_JOINTS = 50;// max number of joints in a skeleton
+	private static final int DIFFUSE_TEX_UNIT = 0;
+
+	private static final String VERTEX_SHADER = "/eu/tankernn/gameEngine/animation/renderer/animatedEntityVertex.glsl";
+	private static final String FRAGMENT_SHADER = "/eu/tankernn/gameEngine/animation/renderer/animatedEntityFragment.glsl";
+
+	protected UniformMatrix projectionViewMatrix = new UniformMatrix("projectionViewMatrix");
+	protected UniformVec3 lightDirection = new UniformVec3("lightDirection");
+	protected UniformMat4Array jointTransforms = new UniformMat4Array("jointTransforms", MAX_JOINTS);
+	private UniformSampler diffuseMap = new UniformSampler("diffuseMap");
+
+	/**
+	 * Creates the shader program for the {@link AnimatedModelRenderer} by
+	 * loading up the vertex and fragment shader code files. It also gets the
+	 * location of all the specified uniform variables, and also indicates that
+	 * the diffuse texture will be sampled from texture unit 0.
+	 */
+	public AnimatedModelShader() {
+		super(VERTEX_SHADER, FRAGMENT_SHADER, "in_position", "in_textureCoords", "in_normal", "in_jointIndices",
+				"in_weights");
+		super.storeAllUniformLocations(projectionViewMatrix, diffuseMap, lightDirection, jointTransforms);
+		connectTextureUnits();
+	}
+
+	/**
+	 * Indicates which texture unit the diffuse texture should be sampled from.
+	 */
+	private void connectTextureUnits() {
+		super.start();
+		diffuseMap.loadTexUnit(DIFFUSE_TEX_UNIT);
+		super.stop();
+	}
+
+}

+ 20 - 0
src/main/java/eu/tankernn/gameEngine/animation/renderer/animatedEntityFragment.glsl

@@ -0,0 +1,20 @@
+#version 150
+
+const vec2 lightBias = vec2(0.7, 0.6);//just indicates the balance between diffuse and ambient lighting
+
+in vec2 pass_textureCoords;
+in vec3 pass_normal;
+
+out vec4 out_colour;
+
+uniform sampler2D diffuseMap;
+uniform vec3 lightDirection;
+
+void main(void){
+	
+	vec4 diffuseColour = texture(diffuseMap, pass_textureCoords);		
+	vec3 unitNormal = normalize(pass_normal);
+	float diffuseLight = max(dot(-lightDirection, unitNormal), 0.0) * lightBias.x + lightBias.y;
+	out_colour = diffuseColour * diffuseLight;
+	
+}

+ 36 - 0
src/main/java/eu/tankernn/gameEngine/animation/renderer/animatedEntityVertex.glsl

@@ -0,0 +1,36 @@
+#version 150
+
+const int MAX_JOINTS = 50;//max joints allowed in a skeleton
+const int MAX_WEIGHTS = 3;//max number of joints that can affect a vertex
+
+in vec3 in_position;
+in vec2 in_textureCoords;
+in vec3 in_normal;
+in ivec3 in_jointIndices;
+in vec3 in_weights;
+
+out vec2 pass_textureCoords;
+out vec3 pass_normal;
+
+uniform mat4 jointTransforms[MAX_JOINTS];
+uniform mat4 projectionViewMatrix;
+
+void main(void){
+	
+	vec4 totalLocalPos = vec4(0.0);
+	vec4 totalNormal = vec4(0.0);
+	
+	for(int i=0;i<MAX_WEIGHTS;i++){
+		mat4 jointTransform = jointTransforms[in_jointIndices[i]];
+		vec4 posePosition = jointTransform * vec4(in_position, 1.0);
+		totalLocalPos += posePosition * in_weights[i];
+		
+		vec4 worldNormal = jointTransform * vec4(in_normal, 0.0);
+		totalNormal += worldNormal * in_weights[i];
+	}
+	
+	gl_Position = projectionViewMatrix * totalLocalPos;
+	pass_normal = totalNormal.xyz;
+	pass_textureCoords = in_textureCoords;
+
+}

+ 21 - 0
src/main/java/eu/tankernn/gameEngine/loader/colladaLoader/AnimatedModelData.java

@@ -0,0 +1,21 @@
+package eu.tankernn.gameEngine.loader.colladaLoader;
+
+public class AnimatedModelData {
+
+	private final JointsData joints;
+	private final MeshData mesh;
+	
+	protected AnimatedModelData(MeshData mesh, JointsData joints){
+		this.joints = joints;
+		this.mesh = mesh;
+	}
+	
+	public JointsData getJointsData(){
+		return joints;
+	}
+	
+	public MeshData getMeshData(){
+		return mesh;
+	}
+	
+}

+ 13 - 0
src/main/java/eu/tankernn/gameEngine/loader/colladaLoader/AnimationData.java

@@ -0,0 +1,13 @@
+package eu.tankernn.gameEngine.loader.colladaLoader;
+
+public class AnimationData {
+	
+	public final float lengthSeconds;
+	public final KeyFrameData[] keyFrames;
+	
+	public AnimationData(float lengthSeconds, KeyFrameData[] keyFrames){
+		this.lengthSeconds = lengthSeconds;
+		this.keyFrames = keyFrames;
+	}
+
+}

+ 101 - 0
src/main/java/eu/tankernn/gameEngine/loader/colladaLoader/AnimationLoader.java

@@ -0,0 +1,101 @@
+package eu.tankernn.gameEngine.loader.colladaLoader;
+
+import java.nio.FloatBuffer;
+import java.util.List;
+
+import org.lwjgl.BufferUtils;
+import org.lwjgl.util.vector.Matrix4f;
+import org.lwjgl.util.vector.Vector3f;
+
+import eu.tankernn.gameEngine.loader.xmlLoader.XmlNode;
+
+
+public class AnimationLoader {
+	
+	private static final Matrix4f CORRECTION = new Matrix4f().rotate((float) Math.toRadians(-90), new Vector3f(1, 0, 0));
+	
+	private XmlNode animationData;
+	private XmlNode jointHierarchy;
+	
+	public AnimationLoader(XmlNode animationData, XmlNode jointHierarchy){
+		this.animationData = animationData;
+		this.jointHierarchy = jointHierarchy;
+	}
+	
+	public AnimationData extractAnimation(){
+		String rootNode = findRootJointName();
+		float[] times = getKeyTimes();
+		float duration = times[times.length-1];
+		KeyFrameData[] keyFrames = initKeyFrames(times);
+		List<XmlNode> animationNodes = animationData.getChildren("animation");
+		for(XmlNode jointNode : animationNodes){
+			loadJointTransforms(keyFrames, jointNode, rootNode);
+		}
+		return new AnimationData(duration, keyFrames);
+	}
+	
+	private float[] getKeyTimes(){
+		XmlNode timeData = animationData.getChild("animation").getChild("source").getChild("float_array");
+		String[] rawTimes = timeData.getData().split(" ");
+		float[] times = new float[rawTimes.length];
+		for(int i=0;i<times.length;i++){
+			times[i] = Float.parseFloat(rawTimes[i]);
+		}
+		return times;
+	}
+	
+	private KeyFrameData[] initKeyFrames(float[] times){
+		KeyFrameData[] frames = new KeyFrameData[times.length];
+		for(int i=0;i<frames.length;i++){
+			frames[i] = new KeyFrameData(times[i]);
+		}
+		return frames;
+	}
+	
+	private void loadJointTransforms(KeyFrameData[] frames, XmlNode jointData, String rootNodeId){
+		String jointNameId = getJointName(jointData);
+		String dataId = getDataId(jointData);
+		XmlNode transformData = jointData.getChildWithAttribute("source", "id", dataId);
+		String[] rawData = transformData.getChild("float_array").getData().split(" ");
+		processTransforms(jointNameId, rawData, frames, jointNameId.equals(rootNodeId));
+	}
+	
+	private String getDataId(XmlNode jointData){
+		XmlNode node = jointData.getChild("sampler").getChildWithAttribute("input", "semantic", "OUTPUT");
+		return node.getAttribute("source").substring(1);
+	}
+	
+	private String getJointName(XmlNode jointData){
+		XmlNode channelNode = jointData.getChild("channel");
+		String data = channelNode.getAttribute("target");
+		return data.split("/")[0];
+	}
+	
+	private void processTransforms(String jointName, String[] rawData, KeyFrameData[] keyFrames, boolean root){
+		FloatBuffer buffer = BufferUtils.createFloatBuffer(16);
+		float[] matrixData = new float[16];
+		for(int i=0;i<keyFrames.length;i++){
+			for(int j=0;j<16;j++){
+				matrixData[j] = Float.parseFloat(rawData[i*16 + j]);
+			}
+			buffer.clear();
+			buffer.put(matrixData);
+			buffer.flip();
+			Matrix4f transform = new Matrix4f();
+			transform.load(buffer);
+			transform.transpose();
+			if(root){
+				//because up axis in Blender is different to up axis in game
+				Matrix4f.mul(CORRECTION, transform, transform);
+			}
+			keyFrames[i].addJointTransform(new JointTransformData(jointName, transform));
+		}
+	}
+	
+	private String findRootJointName(){
+		XmlNode skeleton = jointHierarchy.getChild("visual_scene").getChildWithAttribute("node", "id", "Armature");
+		return skeleton.getChild("node").getAttribute("id");
+	}
+
+
+}

+ 33 - 0
src/main/java/eu/tankernn/gameEngine/loader/colladaLoader/ColladaLoader.java

@@ -0,0 +1,33 @@
+package eu.tankernn.gameEngine.loader.colladaLoader;
+
+import eu.tankernn.gameEngine.loader.xmlLoader.XmlNode;
+import eu.tankernn.gameEngine.loader.xmlLoader.XmlParser;
+import eu.tankernn.gameEngine.util.InternalFile;
+
+public class ColladaLoader {
+
+	public static AnimatedModelData loadColladaModel(InternalFile colladaFile, int maxWeights) {
+		XmlNode node = XmlParser.loadXmlFile(colladaFile);
+
+		SkinLoader skinLoader = new SkinLoader(node.getChild("library_controllers"), maxWeights);
+		SkinningData skinningData = skinLoader.extractSkinData();
+
+		JointsLoader jointsLoader = new JointsLoader(node.getChild("library_visual_scenes"), skinningData.jointOrder);
+		JointsData jointsData = jointsLoader.extractBoneData();
+
+		GeometryLoader g = new GeometryLoader(node.getChild("library_geometries"), skinningData.verticesSkinData);
+		MeshData meshData = g.extractModelData();
+
+		return new AnimatedModelData(meshData, jointsData);
+	}
+
+	public static AnimationData loadColladaAnimation(InternalFile colladaFile) {
+		XmlNode node = XmlParser.loadXmlFile(colladaFile);
+		XmlNode animNode = node.getChild("library_animations");
+		XmlNode jointsNode = node.getChild("library_visual_scenes");
+		AnimationLoader loader = new AnimationLoader(animNode, jointsNode);
+		AnimationData animData = loader.extractAnimation();
+		return animData;
+	}
+
+}

+ 206 - 0
src/main/java/eu/tankernn/gameEngine/loader/colladaLoader/GeometryLoader.java

@@ -0,0 +1,206 @@
+package eu.tankernn.gameEngine.loader.colladaLoader;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.lwjgl.util.vector.Matrix4f;
+import org.lwjgl.util.vector.Vector2f;
+import org.lwjgl.util.vector.Vector3f;
+import org.lwjgl.util.vector.Vector4f;
+
+import eu.tankernn.gameEngine.loader.xmlLoader.XmlNode;
+
+
+/**
+ * Loads the mesh data for a model from a collada XML file.
+ * @author Karl
+ *
+ */
+public class GeometryLoader {
+
+	private final XmlNode meshData;
+	
+	private final List<VertexSkinData> vertexWeights;
+	
+	private float[] verticesArray;
+	private float[] normalsArray;
+	private float[] texturesArray;
+	private int[] indicesArray;
+	private int[] jointIdsArray;
+	private float[] weightsArray;
+
+	List<Vertex> vertices = new ArrayList<Vertex>();
+	List<Vector2f> textures = new ArrayList<Vector2f>();
+	List<Vector3f> normals = new ArrayList<Vector3f>();
+	List<Integer> indices = new ArrayList<Integer>();
+	
+	private Matrix4f correction = new Matrix4f().rotate((float) Math.toRadians(-90), new Vector3f(1, 0,0));
+	
+	public GeometryLoader(XmlNode geometryNode, List<VertexSkinData> vertexWeights) {
+		this.vertexWeights = vertexWeights;
+		this.meshData = geometryNode.getChild("geometry").getChild("mesh");
+	}
+	
+	public MeshData extractModelData(){
+		readRawData();
+		assembleVertices();
+		removeUnusedVertices();
+		initArrays();
+		convertDataToArrays();
+		convertIndicesListToArray();
+		return new MeshData(verticesArray, texturesArray, normalsArray, indicesArray, jointIdsArray, weightsArray, 1);
+	}
+
+	private void readRawData() {
+		readPositions();
+		readNormals();
+		readTextureCoords();
+	}
+
+	private void readPositions() {
+		String positionsId = meshData.getChild("vertices").getChild("input").getAttribute("source").substring(1);
+		XmlNode positionsData = meshData.getChildWithAttribute("source", "id", positionsId).getChild("float_array");
+		int count = Integer.parseInt(positionsData.getAttribute("count"));
+		String[] posData = positionsData.getData().split(" ");
+		for (int i = 0; i < count/3; i++) {
+			float x = Float.parseFloat(posData[i * 3]);
+			float y = Float.parseFloat(posData[i * 3 + 1]);
+			float z = Float.parseFloat(posData[i * 3 + 2]);
+			Vector4f position = new Vector4f(x, y, z, 1);
+			Matrix4f.transform(correction, position, position);
+			vertices.add(new Vertex(vertices.size(), new Vector3f(position.x, position.y, position.z), vertexWeights.get(vertices.size())));
+		}
+	}
+
+	private void readNormals() {
+		String normalsId = meshData.getChild("polylist").getChildWithAttribute("input", "semantic", "NORMAL")
+				.getAttribute("source").substring(1);
+		XmlNode normalsData = meshData.getChildWithAttribute("source", "id", normalsId).getChild("float_array");
+		int count = Integer.parseInt(normalsData.getAttribute("count"));
+		String[] normData = normalsData.getData().split(" ");
+		for (int i = 0; i < count/3; i++) {
+			float x = Float.parseFloat(normData[i * 3]);
+			float y = Float.parseFloat(normData[i * 3 + 1]);
+			float z = Float.parseFloat(normData[i * 3 + 2]);
+			Vector4f norm = new Vector4f(x, y, z, 0f);
+			Matrix4f.transform(correction, norm, norm);
+			normals.add(new Vector3f(norm.x, norm.y, norm.z));
+		}
+	}
+
+	private void readTextureCoords() {
+		String texCoordsId = meshData.getChild("polylist").getChildWithAttribute("input", "semantic", "TEXCOORD")
+				.getAttribute("source").substring(1);
+		XmlNode texCoordsData = meshData.getChildWithAttribute("source", "id", texCoordsId).getChild("float_array");
+		int count = Integer.parseInt(texCoordsData.getAttribute("count"));
+		String[] texData = texCoordsData.getData().split(" ");
+		for (int i = 0; i < count/2; i++) {
+			float s = Float.parseFloat(texData[i * 2]);
+			float t = Float.parseFloat(texData[i * 2 + 1]);
+			textures.add(new Vector2f(s, t));
+		}
+	}
+	
+	private void assembleVertices(){
+		XmlNode poly = meshData.getChild("polylist");
+		int typeCount = poly.getChildren("input").size();
+		String[] indexData = poly.getChild("p").getData().split(" ");
+		for(int i=0;i<indexData.length/typeCount;i++){
+			int positionIndex = Integer.parseInt(indexData[i * typeCount]);
+			int normalIndex = Integer.parseInt(indexData[i * typeCount + 1]);
+			int texCoordIndex = Integer.parseInt(indexData[i * typeCount + 2]);
+			processVertex(positionIndex, normalIndex, texCoordIndex);
+		}
+	}
+	
+
+	private Vertex processVertex(int posIndex, int normIndex, int texIndex) {
+		Vertex currentVertex = vertices.get(posIndex);
+		if (!currentVertex.isSet()) {
+			currentVertex.setTextureIndex(texIndex);
+			currentVertex.setNormalIndex(normIndex);
+			indices.add(posIndex);
+			return currentVertex;
+		} else {
+			return dealWithAlreadyProcessedVertex(currentVertex, texIndex, normIndex);
+		}
+	}
+
+	private int[] convertIndicesListToArray() {
+		this.indicesArray = new int[indices.size()];
+		for (int i = 0; i < indicesArray.length; i++) {
+			indicesArray[i] = indices.get(i);
+		}
+		return indicesArray;
+	}
+
+	private float convertDataToArrays() {
+		float furthestPoint = 0;
+		for (int i = 0; i < vertices.size(); i++) {
+			Vertex currentVertex = vertices.get(i);
+			if (currentVertex.getLength() > furthestPoint) {
+				furthestPoint = currentVertex.getLength();
+			}
+			Vector3f position = currentVertex.getPosition();
+			Vector2f textureCoord = textures.get(currentVertex.getTextureIndex());
+			Vector3f normalVector = normals.get(currentVertex.getNormalIndex());
+			verticesArray[i * 3] = position.x;
+			verticesArray[i * 3 + 1] = position.y;
+			verticesArray[i * 3 + 2] = position.z;
+			texturesArray[i * 2] = textureCoord.x;
+			texturesArray[i * 2 + 1] = 1 - textureCoord.y;
+			normalsArray[i * 3] = normalVector.x;
+			normalsArray[i * 3 + 1] = normalVector.y;
+			normalsArray[i * 3 + 2] = normalVector.z;
+			VertexSkinData weights = currentVertex.getWeightsData();
+			jointIdsArray[i * 3] = weights.jointIds.get(0);
+			jointIdsArray[i * 3 + 1] = weights.jointIds.get(1);
+			jointIdsArray[i * 3 + 2] = weights.jointIds.get(2);
+			weightsArray[i * 3] = weights.weights.get(0);
+			weightsArray[i * 3 + 1] = weights.weights.get(1);
+			weightsArray[i * 3 + 2] = weights.weights.get(2);
+
+		}
+		return furthestPoint;
+	}
+
+	private Vertex dealWithAlreadyProcessedVertex(Vertex previousVertex, int newTextureIndex, int newNormalIndex) {
+		if (previousVertex.hasSameTextureAndNormal(newTextureIndex, newNormalIndex)) {
+			indices.add(previousVertex.getIndex());
+			return previousVertex;
+		} else {
+			Vertex anotherVertex = previousVertex.getDuplicateVertex();
+			if (anotherVertex != null) {
+				return dealWithAlreadyProcessedVertex(anotherVertex, newTextureIndex, newNormalIndex);
+			} else {
+				Vertex duplicateVertex = new Vertex(vertices.size(), previousVertex.getPosition(), previousVertex.getWeightsData());
+				duplicateVertex.setTextureIndex(newTextureIndex);
+				duplicateVertex.setNormalIndex(newNormalIndex);
+				previousVertex.setDuplicateVertex(duplicateVertex);
+				vertices.add(duplicateVertex);
+				indices.add(duplicateVertex.getIndex());
+				return duplicateVertex;
+			}
+
+		}
+	}
+	
+	private void initArrays(){
+		this.verticesArray = new float[vertices.size() * 3];
+		this.texturesArray = new float[vertices.size() * 2];
+		this.normalsArray = new float[vertices.size() * 3];
+		this.jointIdsArray = new int[vertices.size() * 3];
+		this.weightsArray = new float[vertices.size() * 3];
+	}
+
+	private void removeUnusedVertices() {
+		for (Vertex vertex : vertices) {
+			vertex.averageTangents();
+			if (!vertex.isSet()) {
+				vertex.setTextureIndex(0);
+				vertex.setNormalIndex(0);
+			}
+		}
+	}
+	
+}

+ 27 - 0
src/main/java/eu/tankernn/gameEngine/loader/colladaLoader/JointData.java

@@ -0,0 +1,27 @@
+package eu.tankernn.gameEngine.loader.colladaLoader;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.lwjgl.util.vector.Matrix4f;
+
+public class JointData {
+
+	public final int index;
+	public final String nameId;
+	public final Matrix4f bindLocalTransform;
+	
+	public final List<JointData> children = new ArrayList<JointData>();
+	
+	protected JointData(int index, String nameId, Matrix4f bindLocalTransform){
+		this.index = index;
+		this.nameId = nameId;
+		this.bindLocalTransform = bindLocalTransform;
+	}
+	
+	public void addChild(JointData child){
+		children.add(child);
+	}
+	
+	
+}

+ 14 - 0
src/main/java/eu/tankernn/gameEngine/loader/colladaLoader/JointTransformData.java

@@ -0,0 +1,14 @@
+package eu.tankernn.gameEngine.loader.colladaLoader;
+
+import org.lwjgl.util.vector.Matrix4f;
+
+public class JointTransformData {
+
+	public final String jointNameId;
+	public final Matrix4f jointLocalTransform;
+	
+	protected JointTransformData(String jointNameId, Matrix4f jointLocalTransform){
+		this.jointNameId = jointNameId;
+		this.jointLocalTransform = jointLocalTransform;
+	}
+}

+ 13 - 0
src/main/java/eu/tankernn/gameEngine/loader/colladaLoader/JointsData.java

@@ -0,0 +1,13 @@
+package eu.tankernn.gameEngine.loader.colladaLoader;
+
+public class JointsData {
+	
+	public final int jointCount;
+	public final JointData headJoint;
+	
+	protected JointsData(int jointCount, JointData headJoint){
+		this.jointCount = jointCount;
+		this.headJoint = headJoint;
+	}
+
+}

+ 68 - 0
src/main/java/eu/tankernn/gameEngine/loader/colladaLoader/JointsLoader.java

@@ -0,0 +1,68 @@
+package eu.tankernn.gameEngine.loader.colladaLoader;
+
+import java.nio.FloatBuffer;
+import java.util.List;
+
+import org.lwjgl.BufferUtils;
+import org.lwjgl.util.vector.Matrix4f;
+import org.lwjgl.util.vector.Vector3f;
+
+import eu.tankernn.gameEngine.loader.xmlLoader.XmlNode;
+
+
+public class JointsLoader {
+
+	private XmlNode armatureData;
+	
+	private List<String> boneOrder;
+	
+	private int jointCount = 0;
+	
+	private static final Matrix4f CORRECTION = new Matrix4f().rotate((float) Math.toRadians(-90), new Vector3f(1, 0, 0));
+
+	public JointsLoader(XmlNode visualSceneNode, List<String> boneOrder) {
+		this.armatureData = visualSceneNode.getChild("visual_scene").getChildWithAttribute("node", "id", "Armature");
+		this.boneOrder = boneOrder;
+	}
+	
+	public JointsData extractBoneData(){
+		XmlNode headNode = armatureData.getChild("node");
+		JointData headJoint = loadJointData(headNode, true);
+		return new JointsData(jointCount, headJoint);
+	}
+	
+	private JointData loadJointData(XmlNode jointNode, boolean isRoot){
+		JointData joint = extractMainJointData(jointNode, isRoot);
+		for(XmlNode childNode : jointNode.getChildren("node")){
+			joint.addChild(loadJointData(childNode, false));
+		}
+		return joint;
+	}
+	
+	private JointData extractMainJointData(XmlNode jointNode, boolean isRoot){
+		String nameId = jointNode.getAttribute("id");
+		int index = boneOrder.indexOf(nameId);
+		String[] matrixData = jointNode.getChild("matrix").getData().split(" ");
+		Matrix4f matrix = new Matrix4f();
+		matrix.load(convertData(matrixData));
+		matrix.transpose();
+		if(isRoot){
+			//because in Blender z is up, but in our game y is up.
+			Matrix4f.mul(CORRECTION, matrix, matrix);
+		}
+		jointCount++;
+		return new JointData(index, nameId, matrix);
+	}
+	
+	private FloatBuffer convertData(String[] rawData){
+		float[] matrixData = new float[16];
+		for(int i=0;i<matrixData.length;i++){
+			matrixData[i] = Float.parseFloat(rawData[i]);
+		}
+		FloatBuffer buffer = BufferUtils.createFloatBuffer(16);
+		buffer.put(matrixData);
+		buffer.flip();
+		return buffer;
+	}
+
+}

+ 19 - 0
src/main/java/eu/tankernn/gameEngine/loader/colladaLoader/KeyFrameData.java

@@ -0,0 +1,19 @@
+package eu.tankernn.gameEngine.loader.colladaLoader;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class KeyFrameData {
+
+	public final float time;
+	public final List<JointTransformData> jointTransforms = new ArrayList<JointTransformData>();
+	
+	protected KeyFrameData(float time){
+		this.time = time;
+	}
+	
+	protected void addJointTransform(JointTransformData transform){
+		jointTransforms.add(transform);
+	}
+	
+}

+ 58 - 0
src/main/java/eu/tankernn/gameEngine/loader/colladaLoader/MeshData.java

@@ -0,0 +1,58 @@
+package eu.tankernn.gameEngine.loader.colladaLoader;
+
+public class MeshData {
+
+	private static final int DIMENSIONS = 3;
+
+	private float[] vertices;
+	private float[] textureCoords;
+	private float[] normals;
+	private int[] indices;
+	private int[] jointIds;
+	private float[] vertexWeights;
+	private float furthestPoint;
+
+	public MeshData(float[] vertices, float[] textureCoords, float[] normals, int[] indices,
+			int[] jointIds, float[] vertexWeights, float furthestPoint) {
+		this.vertices = vertices;
+		this.textureCoords = textureCoords;
+		this.normals = normals;
+		this.indices = indices;
+		this.jointIds = jointIds;
+		this.vertexWeights = vertexWeights;
+		this.furthestPoint = furthestPoint;
+	}
+
+	public int[] getJointIds() {
+		return jointIds;
+	}
+	
+	public float[] getVertexWeights(){
+		return vertexWeights;
+	}
+
+	public int getVertexCount() {
+		return vertices.length / DIMENSIONS;
+	}
+
+	public float[] getVertices() {
+		return vertices;
+	}
+
+	public float[] getTextureCoords() {
+		return textureCoords;
+	}
+
+	public float[] getNormals() {
+		return normals;
+	}
+
+	public int[] getIndices() {
+		return indices;
+	}
+
+	public float getFurthestPoint() {
+		return furthestPoint;
+	}
+
+}

+ 77 - 0
src/main/java/eu/tankernn/gameEngine/loader/colladaLoader/SkinLoader.java

@@ -0,0 +1,77 @@
+package eu.tankernn.gameEngine.loader.colladaLoader;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import eu.tankernn.gameEngine.loader.xmlLoader.XmlNode;
+
+public class SkinLoader {
+
+	private final XmlNode skinningData;
+	private final int maxWeights;
+
+	public SkinLoader(XmlNode controllersNode, int maxWeights) {
+		this.skinningData = controllersNode.getChild("controller").getChild("skin");
+		this.maxWeights = maxWeights;
+	}
+
+	public SkinningData extractSkinData() {
+		List<String> jointsList = loadJointsList();
+		float[] weights = loadWeights();
+		XmlNode weightsDataNode = skinningData.getChild("vertex_weights");
+		int[] effectorJointCounts = getEffectiveJointsCounts(weightsDataNode);
+		List<VertexSkinData> vertexWeights = getSkinData(weightsDataNode, effectorJointCounts, weights);
+		return new SkinningData(jointsList, vertexWeights);
+	}
+
+	private List<String> loadJointsList() {
+		XmlNode inputNode = skinningData.getChild("vertex_weights");
+		String jointDataId = inputNode.getChildWithAttribute("input", "semantic", "JOINT").getAttribute("source").substring(1);
+		XmlNode jointsNode = skinningData.getChildWithAttribute("source", "id", jointDataId).getChild("Name_array");
+		String[] names = jointsNode.getData().split(" ");
+		List<String> jointsList = new ArrayList<String>();
+		for (String name : names) {
+			jointsList.add(name);
+		}
+		return jointsList;
+	}
+
+	private float[] loadWeights() {
+		XmlNode inputNode = skinningData.getChild("vertex_weights");
+		String weightsDataId = inputNode.getChildWithAttribute("input", "semantic", "WEIGHT").getAttribute("source").substring(1);
+		XmlNode weightsNode = skinningData.getChildWithAttribute("source", "id", weightsDataId).getChild("float_array");
+		String[] rawData = weightsNode.getData().split(" ");
+		float[] weights = new float[rawData.length];
+		for (int i = 0; i < weights.length; i++) {
+			weights[i] = Float.parseFloat(rawData[i]);
+		}
+		return weights;
+	}
+
+	private int[] getEffectiveJointsCounts(XmlNode weightsDataNode) {
+		String[] rawData = weightsDataNode.getChild("vcount").getData().split(" ");
+		int[] counts = new int[rawData.length];
+		for (int i = 0; i < rawData.length; i++) {
+			counts[i] = Integer.parseInt(rawData[i]);
+		}
+		return counts;
+	}
+
+	private List<VertexSkinData> getSkinData(XmlNode weightsDataNode, int[] counts, float[] weights) {
+		String[] rawData = weightsDataNode.getChild("v").getData().split(" ");
+		List<VertexSkinData> skinningData = new ArrayList<VertexSkinData>();
+		int pointer = 0;
+		for (int count : counts) {
+			VertexSkinData skinData = new VertexSkinData();
+			for (int i = 0; i < count; i++) {
+				int jointId = Integer.parseInt(rawData[pointer++]);
+				int weightId = Integer.parseInt(rawData[pointer++]);
+				skinData.addJointEffect(jointId, weights[weightId]);
+			}
+			skinData.limitJointNumber(maxWeights);
+			skinningData.add(skinData);
+		}
+		return skinningData;
+	}
+
+}

+ 16 - 0
src/main/java/eu/tankernn/gameEngine/loader/colladaLoader/SkinningData.java

@@ -0,0 +1,16 @@
+package eu.tankernn.gameEngine.loader.colladaLoader;
+
+import java.util.List;
+
+public class SkinningData {
+	
+	public final List<String> jointOrder;
+	public final List<VertexSkinData> verticesSkinData;
+	
+	protected SkinningData(List<String> jointOrder, List<VertexSkinData> verticesSkinData){
+		this.jointOrder = jointOrder;
+		this.verticesSkinData = verticesSkinData;
+	}
+
+
+}

+ 97 - 0
src/main/java/eu/tankernn/gameEngine/loader/colladaLoader/Vertex.java

@@ -0,0 +1,97 @@
+package eu.tankernn.gameEngine.loader.colladaLoader;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.lwjgl.util.vector.Vector3f;
+
+public class Vertex {
+	
+	private static final int NO_INDEX = -1;
+	
+	private Vector3f position;
+	private int textureIndex = NO_INDEX;
+	private int normalIndex = NO_INDEX;
+	private Vertex duplicateVertex = null;
+	private int index;
+	private float length;
+	private List<Vector3f> tangents = new ArrayList<Vector3f>();
+	private Vector3f averagedTangent = new Vector3f(0, 0, 0);
+	
+	
+	private VertexSkinData weightsData;
+	
+	public Vertex(int index,Vector3f position, VertexSkinData weightsData){
+		this.index = index;
+		this.weightsData = weightsData;
+		this.position = position;
+		this.length = position.length();
+	}
+	
+	public VertexSkinData getWeightsData(){
+		return weightsData;
+	}
+	
+	public void addTangent(Vector3f tangent){
+		tangents.add(tangent);
+	}
+	
+	public void averageTangents(){
+		if(tangents.isEmpty()){
+			return;
+		}
+		for(Vector3f tangent : tangents){
+			Vector3f.add(averagedTangent, tangent, averagedTangent);
+		}
+		averagedTangent.normalise();
+	}
+	
+	public Vector3f getAverageTangent(){
+		return averagedTangent;
+	}
+	
+	public int getIndex(){
+		return index;
+	}
+	
+	public float getLength(){
+		return length;
+	}
+	
+	public boolean isSet(){
+		return textureIndex!=NO_INDEX && normalIndex!=NO_INDEX;
+	}
+	
+	public boolean hasSameTextureAndNormal(int textureIndexOther,int normalIndexOther){
+		return textureIndexOther==textureIndex && normalIndexOther==normalIndex;
+	}
+	
+	public void setTextureIndex(int textureIndex){
+		this.textureIndex = textureIndex;
+	}
+	
+	public void setNormalIndex(int normalIndex){
+		this.normalIndex = normalIndex;
+	}
+
+	public Vector3f getPosition() {
+		return position;
+	}
+
+	public int getTextureIndex() {
+		return textureIndex;
+	}
+
+	public int getNormalIndex() {
+		return normalIndex;
+	}
+
+	public Vertex getDuplicateVertex() {
+		return duplicateVertex;
+	}
+
+	public void setDuplicateVertex(Vertex duplicateVertex) {
+		this.duplicateVertex = duplicateVertex;
+	}
+
+}

+ 65 - 0
src/main/java/eu/tankernn/gameEngine/loader/colladaLoader/VertexSkinData.java

@@ -0,0 +1,65 @@
+package eu.tankernn.gameEngine.loader.colladaLoader;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class VertexSkinData {
+	
+	public final List<Integer> jointIds = new ArrayList<Integer>();
+	public final List<Float> weights = new ArrayList<Float>();
+	
+	protected void addJointEffect(int jointId, float weight){
+		for(int i=0;i<weights.size();i++){
+			if(weight > weights.get(i)){
+				jointIds.add(i, jointId);
+				weights.add(i, weight);
+				return;
+			}
+		}
+		jointIds.add(jointId);
+		weights.add(weight);
+	}
+	
+	protected void limitJointNumber(int max){
+		if(jointIds.size() > max){
+			float[] topWeights = new float[max];
+			float total = saveTopWeights(topWeights);
+			refillWeightList(topWeights, total);
+			removeExcessJointIds(max);
+		}else if(jointIds.size() < max){
+			fillEmptyWeights(max);
+		}
+	}
+
+	private void fillEmptyWeights(int max){
+		while(jointIds.size() < max){
+			jointIds.add(0);
+			weights.add(0f);
+		}
+	}
+	
+	private float saveTopWeights(float[] topWeightsArray){
+		float total = 0;
+		for(int i=0;i<topWeightsArray.length;i++){
+			topWeightsArray[i] = weights.get(i);
+			total += topWeightsArray[i];
+		}
+		return total;
+	}
+	
+	private void refillWeightList(float[] topWeights, float total){
+		weights.clear();
+		for(int i=0;i<topWeights.length;i++){
+			weights.add(Math.min(topWeights[i]/total, 1));
+		}
+	}
+	
+	private void removeExcessJointIds(int max){
+		while(jointIds.size() > max){
+			jointIds.remove(jointIds.size()-1);
+		}
+	}
+	
+	
+
+}

+ 168 - 0
src/main/java/eu/tankernn/gameEngine/loader/xmlLoader/XmlNode.java

@@ -0,0 +1,168 @@
+package eu.tankernn.gameEngine.loader.xmlLoader;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Represents a node in an XML file. This contains the name of the node, a map
+ * of the attributes and their values, any text data between the start and end
+ * tag, and a list of all its children nodes.
+ * 
+ * @author Karl
+ *
+ */
+public class XmlNode {
+
+	private String name;
+	private Map<String, String> attributes;
+	private String data;
+	private Map<String, List<XmlNode>> childNodes;
+
+	protected XmlNode(String name) {
+		this.name = name;
+	}
+
+	/**
+	 * @return The name of the XML node.
+	 */
+	public String getName() {
+		return name;
+	}
+
+	/**
+	 * @return Any text data contained between the start and end tag of the
+	 *         node.
+	 */
+	public String getData() {
+		return data;
+	}
+
+	/**
+	 * Gets the value of a certain attribute of the node. Returns {@code null}
+	 * if the attribute doesn't exist.
+	 * 
+	 * @param attr
+	 *            - the name of the attribute.
+	 * @return The value of the attribute.
+	 */
+	public String getAttribute(String attr) {
+		if (attributes != null) {
+			return attributes.get(attr);
+		} else {
+			return null;
+		}
+	}
+
+	/**
+	 * Gets a certain child node of this node.
+	 * 
+	 * @param childName
+	 *            - the name of the child node.
+	 * @return The child XML node with the given name.
+	 */
+	public XmlNode getChild(String childName) {
+		if (childNodes != null) {
+			List<XmlNode> nodes = childNodes.get(childName);
+			if (nodes != null && !nodes.isEmpty()) {
+				return nodes.get(0);
+			}
+		}
+		return null;
+
+	}
+
+	/**
+	 * Gets a child node with a certain name, and with a given value of a given
+	 * attribute. Used to get a specific child when there are multiple child
+	 * nodes with the same node name.
+	 * 
+	 * @param childName
+	 *            - the name of the child node.
+	 * @param attr
+	 *            - the attribute whose value is to be checked.
+	 * @param value
+	 *            - the value that the attribute must have.
+	 * @return The child node which has the correct name and the correct value
+	 *         for the chosen attribute.
+	 */
+	public XmlNode getChildWithAttribute(String childName, String attr, String value) {
+		List<XmlNode> children = getChildren(childName);
+		if (children == null || children.isEmpty()) {
+			return null;
+		}
+		for (XmlNode child : children) {
+			String val = child.getAttribute(attr);
+			if (value.equals(val)) {
+				return child;
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * Get the child nodes of this node that have a given name.
+	 * 
+	 * @param name
+	 *            - the name of the child nodes.
+	 * @return A list of the child nodes with the given name. If none exist then
+	 *         an empty list is returned.
+	 */
+	public List<XmlNode> getChildren(String name) {
+		if (childNodes != null) {
+			List<XmlNode> children = childNodes.get(name);
+			if (children != null) {
+				return children;
+			}
+		}
+		return new ArrayList<XmlNode>();
+	}
+
+	/**
+	 * Adds a new attribute to this node. An attribute has a name and a value.
+	 * Attributes are stored in a HashMap which is initialized in here if it was
+	 * previously null.
+	 * 
+	 * @param attr
+	 *            - the name of the attribute.
+	 * @param value
+	 *            - the value of the attribute.
+	 */
+	protected void addAttribute(String attr, String value) {
+		if (attributes == null) {
+			attributes = new HashMap<String, String>();
+		}
+		attributes.put(attr, value);
+	}
+
+	/**
+	 * Adds a child node to this node.
+	 * 
+	 * @param child
+	 *            - the child node to add.
+	 */
+	protected void addChild(XmlNode child) {
+		if (childNodes == null) {
+			childNodes = new HashMap<String, List<XmlNode>>();
+		}
+		List<XmlNode> list = childNodes.get(child.name);
+		if (list == null) {
+			list = new ArrayList<XmlNode>();
+			childNodes.put(child.name, list);
+		}
+		list.add(child);
+	}
+
+	/**
+	 * Sets some data for this node.
+	 * 
+	 * @param data
+	 *            - the data for this node (text that is found between the start
+	 *            and end tags of this node).
+	 */
+	protected void setData(String data) {
+		this.data = data;
+	}
+
+}

+ 102 - 0
src/main/java/eu/tankernn/gameEngine/loader/xmlLoader/XmlParser.java

@@ -0,0 +1,102 @@
+package eu.tankernn.gameEngine.loader.xmlLoader;
+
+import java.io.BufferedReader;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import eu.tankernn.gameEngine.util.InternalFile;
+
+/**
+ * Reads an XML file and stores all the data in {@link XmlNode} objects,
+ * allowing for easy access to the data contained in the XML file.
+ * 
+ * @author Karl
+ *
+ */
+public class XmlParser {
+
+	private static final Pattern DATA = Pattern.compile(">(.+?)<");
+	private static final Pattern START_TAG = Pattern.compile("<(.+?)>");
+	private static final Pattern ATTR_NAME = Pattern.compile("(.+?)=");
+	private static final Pattern ATTR_VAL = Pattern.compile("\"(.+?)\"");
+	private static final Pattern CLOSED = Pattern.compile("(</|/>)");
+
+	/**
+	 * Reads an XML file and stores all the data in {@link XmlNode} objects,
+	 * allowing for easy access to the data contained in the XML file.
+	 * 
+	 * @param file - the XML file
+	 * @return The root node of the XML structure.
+	 */
+	public static XmlNode loadXmlFile(InternalFile file) {
+		BufferedReader reader = null;
+		try {
+			reader = file.getReader();
+		} catch (Exception e) {
+			e.printStackTrace();
+			System.err.println("Can't find the XML file: " + file.getPath());
+			System.exit(0);
+			return null;
+		}
+		try {
+			reader.readLine();
+			XmlNode node = loadNode(reader);
+			reader.close();
+			return node;
+		} catch (Exception e) {
+			e.printStackTrace();
+			System.err.println("Error with XML file format for: " + file.getPath());
+			System.exit(0);
+			return null;
+		}
+	}
+
+	private static XmlNode loadNode(BufferedReader reader) throws Exception {
+		String line = reader.readLine().trim();
+		if (line.startsWith("</")) {
+			return null;
+		}
+		String[] startTagParts = getStartTag(line).split(" ");
+		XmlNode node = new XmlNode(startTagParts[0].replace("/", ""));
+		addAttributes(startTagParts, node);
+		addData(line, node);
+		if (CLOSED.matcher(line).find()) {
+			return node;
+		}
+		XmlNode child = null;
+		while ((child = loadNode(reader)) != null) {
+			node.addChild(child);
+		}
+		return node;
+	}
+
+	private static void addData(String line, XmlNode node) {
+		Matcher matcher = DATA.matcher(line);
+		if (matcher.find()) {
+			node.setData(matcher.group(1));
+		}
+	}
+
+	private static void addAttributes(String[] titleParts, XmlNode node) {
+		for (int i = 1; i < titleParts.length; i++) {
+			if (titleParts[i].contains("=")) {
+				addAttribute(titleParts[i], node);
+			}
+		}
+	}
+
+	private static void addAttribute(String attributeLine, XmlNode node) {
+		Matcher nameMatch = ATTR_NAME.matcher(attributeLine);
+		nameMatch.find();
+		Matcher valMatch = ATTR_VAL.matcher(attributeLine);
+		valMatch.find();
+		node.addAttribute(nameMatch.group(1), valMatch.group(1));
+	}
+
+	private static String getStartTag(String line) {
+		Matcher match = START_TAG.matcher(line);
+		match.find();
+		return match.group(1);
+	}
+
+}

+ 1 - 1
src/main/java/eu/tankernn/gameEngine/postProcessing/bloom/BrightFilter.java

@@ -1,8 +1,8 @@
 package eu.tankernn.gameEngine.postProcessing.bloom;
 
 import eu.tankernn.gameEngine.loader.textures.Texture;
-import eu.tankernn.gameEngine.postProcessing.PostProcessingEffect;
 import eu.tankernn.gameEngine.postProcessing.ImageRenderer;
+import eu.tankernn.gameEngine.postProcessing.PostProcessingEffect;
 
 public class BrightFilter extends PostProcessingEffect<BrightFilterShader> {
 

+ 1 - 1
src/main/java/eu/tankernn/gameEngine/postProcessing/gaussianBlur/HorizontalBlur.java

@@ -1,8 +1,8 @@
 package eu.tankernn.gameEngine.postProcessing.gaussianBlur;
 
 import eu.tankernn.gameEngine.loader.textures.Texture;
-import eu.tankernn.gameEngine.postProcessing.PostProcessingEffect;
 import eu.tankernn.gameEngine.postProcessing.ImageRenderer;
+import eu.tankernn.gameEngine.postProcessing.PostProcessingEffect;
 
 public class HorizontalBlur extends PostProcessingEffect<HorizontalBlurShader> {
 	

+ 1 - 1
src/main/java/eu/tankernn/gameEngine/postProcessing/gaussianBlur/VerticalBlur.java

@@ -1,8 +1,8 @@
 package eu.tankernn.gameEngine.postProcessing.gaussianBlur;
 
 import eu.tankernn.gameEngine.loader.textures.Texture;
-import eu.tankernn.gameEngine.postProcessing.PostProcessingEffect;
 import eu.tankernn.gameEngine.postProcessing.ImageRenderer;
+import eu.tankernn.gameEngine.postProcessing.PostProcessingEffect;
 
 public class VerticalBlur extends PostProcessingEffect<VerticalBlurShader> {
 	

+ 88 - 0
src/main/java/eu/tankernn/gameEngine/renderEngine/Vao.java

@@ -0,0 +1,88 @@
+package eu.tankernn.gameEngine.renderEngine;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.lwjgl.opengl.GL11;
+import org.lwjgl.opengl.GL15;
+import org.lwjgl.opengl.GL20;
+import org.lwjgl.opengl.GL30;
+
+public class Vao {
+	
+	private static final int BYTES_PER_FLOAT = 4;
+	private static final int BYTES_PER_INT = 4;
+	public final int id;
+	private List<Vbo> dataVbos = new ArrayList<Vbo>();
+	private Vbo indexVbo;
+	private int indexCount;
+
+	public static Vao create() {
+		int id = GL30.glGenVertexArrays();
+		return new Vao(id);
+	}
+
+	private Vao(int id) {
+		this.id = id;
+	}
+	
+	public int getIndexCount(){
+		return indexCount;
+	}
+
+	public void bind(int... attributes){
+		bind();
+		for (int i : attributes) {
+			GL20.glEnableVertexAttribArray(i);
+		}
+	}
+
+	public void unbind(int... attributes){
+		for (int i : attributes) {
+			GL20.glDisableVertexAttribArray(i);
+		}
+		unbind();
+	}
+	
+	public void createIndexBuffer(int[] indices){
+		this.indexVbo = Vbo.create(GL15.GL_ELEMENT_ARRAY_BUFFER);
+		indexVbo.bind();
+		indexVbo.storeData(indices);
+		this.indexCount = indices.length;
+	}
+
+	public void createAttribute(int attribute, float[] data, int attrSize){
+		Vbo dataVbo = Vbo.create(GL15.GL_ARRAY_BUFFER);
+		dataVbo.bind();
+		dataVbo.storeData(data);
+		GL20.glVertexAttribPointer(attribute, attrSize, GL11.GL_FLOAT, false, attrSize * BYTES_PER_FLOAT, 0);
+		dataVbo.unbind();
+		dataVbos.add(dataVbo);
+	}
+	
+	public void createIntAttribute(int attribute, int[] data, int attrSize){
+		Vbo dataVbo = Vbo.create(GL15.GL_ARRAY_BUFFER);
+		dataVbo.bind();
+		dataVbo.storeData(data);
+		GL30.glVertexAttribIPointer(attribute, attrSize, GL11.GL_INT, attrSize * BYTES_PER_INT, 0);
+		dataVbo.unbind();
+		dataVbos.add(dataVbo);
+	}
+	
+	public void delete() {
+		GL30.glDeleteVertexArrays(id);
+		for(Vbo vbo : dataVbos){
+			vbo.delete();
+		}
+		indexVbo.delete();
+	}
+
+	private void bind() {
+		GL30.glBindVertexArray(id);
+	}
+
+	private void unbind() {
+		GL30.glBindVertexArray(0);
+	}
+
+}

+ 32 - 0
src/main/java/eu/tankernn/gameEngine/renderEngine/shaders/UniformMat4Array.java

@@ -0,0 +1,32 @@
+package eu.tankernn.gameEngine.renderEngine.shaders;
+
+import org.lwjgl.util.vector.Matrix4f;
+
+public class UniformMat4Array extends Uniform{
+	
+	private UniformMatrix[] matrixUniforms;
+	
+	public UniformMat4Array(String name, int size) {
+		super(name);
+		matrixUniforms = new UniformMatrix[size];
+		for(int i=0;i<size;i++){
+			matrixUniforms[i] = new UniformMatrix(name + "["+i+"]");
+		}
+	}
+	
+	@Override
+	protected void storeUniformLocation(int programID) {
+		for(UniformMatrix matrixUniform : matrixUniforms){
+			matrixUniform.storeUniformLocation(programID);
+		}
+	}
+
+	public void loadMatrixArray(Matrix4f[] matrices){
+		for(int i=0;i<matrices.length;i++){
+			matrixUniforms[i].loadMatrix(matrices[i]);
+		}
+	}
+	
+	
+
+}

+ 88 - 0
src/main/java/eu/tankernn/gameEngine/util/OpenGlUtils.java

@@ -0,0 +1,88 @@
+package eu.tankernn.gameEngine.util;
+
+import org.lwjgl.opengl.GL11;
+import org.lwjgl.opengl.GL13;
+
+/**
+ * Useful class for updating OpenGL state, such as alpha blending, depth testing, etc.
+ * 
+ * @author Karl
+ *
+ */
+public class OpenGlUtils {
+	
+	private static boolean cullingBackFace = false;
+	private static boolean inWireframe = false;
+	private static boolean isAlphaBlending = false;
+	private static boolean additiveBlending = false;
+	private static boolean antialiasing = false;
+	private static boolean depthTesting = false;
+
+	public static void antialias(boolean enable) {
+		if (enable && !antialiasing) {
+			GL11.glEnable(GL13.GL_MULTISAMPLE);
+			antialiasing = true;
+		} else if (!enable && antialiasing) {
+			GL11.glDisable(GL13.GL_MULTISAMPLE);
+			antialiasing = false;
+		}
+	}
+
+	public static void enableAlphaBlending() {
+		if (!isAlphaBlending) {
+			GL11.glEnable(GL11.GL_BLEND);
+			GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
+			isAlphaBlending = true;
+			additiveBlending = false;
+		}
+	}
+
+	public static void enableAdditiveBlending() {
+		if (!additiveBlending) {
+			GL11.glEnable(GL11.GL_BLEND);
+			GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
+			additiveBlending = true;
+			isAlphaBlending = false;
+		}
+	}
+
+	public static void disableBlending() {
+		if (isAlphaBlending || additiveBlending) {
+			GL11.glDisable(GL11.GL_BLEND);
+			isAlphaBlending = false;
+			additiveBlending = false;
+		}
+	}
+	
+	public static void enableDepthTesting(boolean enable){
+		if(enable && !depthTesting){
+			GL11.glEnable(GL11.GL_DEPTH_TEST);
+			depthTesting = true;
+		}else if(!enable && depthTesting){
+			GL11.glDisable(GL11.GL_DEPTH_TEST);
+			depthTesting = false;
+		}
+	}
+
+	public static void cullBackFaces(boolean cull) {
+		if (cull && !cullingBackFace) {
+			GL11.glEnable(GL11.GL_CULL_FACE);
+			GL11.glCullFace(GL11.GL_BACK);
+			cullingBackFace = true;
+		} else if (!cull && cullingBackFace) {
+			GL11.glDisable(GL11.GL_CULL_FACE);
+			cullingBackFace = false;
+		}
+	}
+
+	public static void goWireframe(boolean goWireframe) {
+		if (goWireframe && !inWireframe) {
+			GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE);
+			inWireframe = true;
+		} else if (!goWireframe && inWireframe) {
+			GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
+			inWireframe = false;
+		}
+	}
+
+}