Commits

Roi Atalla committed 7e5b363

Everything so far. Successfully implemented instancing for cubes and bullets.

  • Participants
  • Parent commits 2721400

Comments (0)

Files changed (19)

File src/com/ra4king/fps/Bullet.java

-package com.ra4king.fps;
-
-import com.ra4king.opengl.util.math.Vector3;
-
-/**
- * @author ra4king
- */
-public class Bullet {
-	public static float SIZE = 3;
-	
-	private final Vector3 position, velocity;
-	
-	private Vector3 color;
-	
-	private long alive;
-	
-	private boolean isSolid;
-	
-	private final long life;
-	
-	public Bullet(Vector3 position, Vector3 velocity) {
-		this(position, velocity, (long)5e9);
-	}
-	
-	public Bullet(Vector3 position, Vector3 velocity, long lifeTime) {
-		this(position, velocity, lifeTime, true);
-	}
-	
-	public Bullet(Vector3 position, Vector3 velocity, long lifeTime, boolean isSolid) {
-		this(position, velocity, lifeTime, isSolid, Math.random() < 0.5 ? new Vector3(0, 0, 1) : new Vector3(1, 165f / 255f, 0));//new Vector3((float)Math.random(), (float)Math.random(), (float)Math.random())); //Math.random() < 0.5 ? new Vector3(1, 0, 1) : new Vector3(57f / 255f, 1, 20f / 255f));//
-	}
-	
-	public Bullet(Vector3 position, Vector3 velocity, long lifeTime, boolean isSolid, Vector3 color) {
-		this.position = position.copy();
-		this.velocity = velocity.copy();
-		life = lifeTime;
-
-		this.isSolid = isSolid;
-		
-		this.color = color.copy();
-	}
-	
-	public boolean isAlive() {
-		return alive < life;
-	}
-	
-	public boolean isSolid() {
-		return isSolid;
-	}
-	
-	public float getAlpha() {
-		return (float)(life - alive) / life;
-	}
-	
-	public Vector3 getPosition() {
-		return position;
-	}
-	
-	public Vector3 getVelocity() {
-		return velocity;
-	}
-	
-	public Vector3 getColor() {
-		return color;
-	}
-	
-	private final Vector3 temp = new Vector3();
-	
-	public void update(long deltaTime) {
-		alive += deltaTime;
-		
-		position.add(temp.set(velocity).mult(deltaTime / (float)1e9));
-	}
-	
-	@Override
-	public String toString() {
-		return "Bullet @ " + position;
-	}
-}

File src/com/ra4king/fps/BulletManager.java

-package com.ra4king.fps;
-
-import static com.ra4king.fps.FPS.*;
-import static org.lwjgl.opengl.GL11.*;
-import static org.lwjgl.opengl.GL15.*;
-import static org.lwjgl.opengl.GL20.*;
-import static org.lwjgl.opengl.GL30.*;
-import static org.lwjgl.opengl.GL31.*;
-
-import java.nio.FloatBuffer;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-
-import org.lwjgl.BufferUtils;
-import org.lwjgl.opengl.APPLEVertexArrayObject;
-import org.lwjgl.opengl.ARBVertexArrayObject;
-
-import com.ra4king.fps.FPS.*;
-import com.ra4king.opengl.util.ShaderProgram;
-import com.ra4king.opengl.util.Utils;
-import com.ra4king.opengl.util.math.Matrix4;
-import com.ra4king.opengl.util.math.MatrixStack;
-import com.ra4king.opengl.util.math.Vector3;
-
-/**
- * @author ra4king
- */
-public class BulletManager {
-	private ArrayList<Bullet> bullets;
-	
-	private ChunkManager chunkManager;
-	
-	private HashMap<Bullet,Vector3> cameraWorldPositions = new HashMap<>();
-	
-	public static final int MAX_BULLET_COUNT = GLUtils.get().VERSION >= 33 ? 2000 : 100;
-	public static final long BULLET_LIFE = (long)8e9;
-	
-	private final BulletRenderer renderer = new BulletRenderer();
-	
-	public BulletManager(ChunkManager chunkManager) {
-		this.chunkManager = chunkManager;
-		
-		bullets = new ArrayList<>();
-	}
-	
-	public void addBullet(Bullet bullet) {
-		bullets.add(bullet);
-		
-		cameraWorldPositions.clear();
-	}
-	
-	public int getBulletCount() {
-		return bullets.size();
-	}
-	
-	public void update(long deltaTime) {
-		ArrayList<Bullet> temp = new ArrayList<>();
-		
-		for(Bullet bullet : bullets) {
-			bullet.update(deltaTime);
-			
-			if(bullet.isAlive()) {
-				boolean isAlive = true;
-				
-				if(false) {// bullet.isSolid()) {
-					Vector3 pos = bullet.getPosition();
-					
-					if(chunkManager.getBlock(pos, 0.5f * Bullet.SIZE) != null) {
-						for(int a = -1; a < 2; a++) {
-							for(int b = -1; b < 2; b++) {
-								for(int c = -1; c < 2; c++) {
-									if(a == 0 && b == 0 && c == 0)
-										continue;
-									
-									temp.add(new Bullet(pos, new Vector3(a, b, c).normalize().mult(100), (long)5e8, false));
-									
-									isAlive = false;
-								}
-							}
-						}
-					}
-				}
-				
-				if(isAlive)
-					temp.add(bullet);
-			}
-		}
-		
-		bullets.clear();
-		bullets = temp;
-		
-		cameraWorldPositions.clear();
-	}
-	
-	public int getBulletLightData(Matrix4 viewMatrix, FloatBuffer lightPositions, FloatBuffer lightColors) {
-		int bulletCount = 0;
-		
-		final float bulletK = 0.0001f, nonSolidBulletK = 0.05f;
-		
-		sort(viewMatrix);
-		
-		for(Bullet b : bullets) {
-			if(bulletCount < MAX_BULLET_COUNT) {
-				bulletCount++;
-				
-				lightPositions.put(cameraWorldPositions.get(b).toBuffer());
-				lightPositions.put((b.isSolid() ? bulletK : nonSolidBulletK) / b.getAlpha());
-				
-				lightColors.put(b.getColor().toBuffer()).put(1);
-			}
-		}
-		
-		return bulletCount;
-	}
-	
-	private void sort(Matrix4 viewMatrix) {
-		if(cameraWorldPositions.isEmpty())
-			sort(viewMatrix, bullets, cameraWorldPositions);
-	}
-	
-	private void sort(Matrix4 viewMatrix, List<Bullet> bullets, final HashMap<Bullet,Vector3> bulletPositions) {
-		if(bulletPositions.isEmpty())
-			for(Bullet b : bullets)
-				bulletPositions.put(b, viewMatrix.mult(b.getPosition()));
-		
-		Collections.sort(bullets, new Comparator<Bullet>() {
-			@Override
-			public int compare(Bullet o1, Bullet o2) {
-				return (int)Math.signum(bulletPositions.get(o1).z() - bulletPositions.get(o2).z());
-			}
-		});
-	}
-	
-	public void render(Matrix4 projectionMatrix, MatrixStack modelViewMatrix, FrustumCulling culling) {
-		sort(modelViewMatrix.getTop());
-		
-		renderer.render(projectionMatrix, modelViewMatrix, bullets, culling);
-	}
-	
-	public void render(Matrix4 projectionMatrix, MatrixStack modelViewMatrix, FrustumCulling culling, Bullet ... bullets) {
-		List<Bullet> bulletList = Arrays.asList(bullets);
-		sort(modelViewMatrix.getTop(), bulletList, new HashMap<Bullet,Vector3>());
-		
-		renderer.render(projectionMatrix, modelViewMatrix, bulletList, culling);
-	}
-	
-	private class BulletRenderer {
-		private ShaderProgram bulletProgram;
-		private int projectionMatrixUniform, modelViewMatrixUniform;
-		private int bulletDataUniform;
-		
-		private FloatBuffer bulletDataBuffer;
-		private int vao, bulletDataVBO;
-		
-		private final boolean IS_MAC = System.getProperty("os.name").toLowerCase().contains("mac");
-		private final boolean HAS_VAO = GLUtils.get().VERSION >= 30;
-		
-		private BulletRenderer() {
-			bulletDataBuffer = BufferUtils.createFloatBuffer(MAX_BULLET_COUNT * 2 * 4);
-			
-			vao = glGenVertexArrays();
-			
-			if(GLUtils.get().VERSION >= 33)
-				bulletProgram = new ShaderProgram(Utils.readFully(getClass().getResourceAsStream(SHADERS_ROOT_PATH + "bullet.vert")), Utils.readFully(getClass().getResourceAsStream(SHADERS_ROOT_PATH + "bullet.frag")));
-			else if(GLUtils.get().VERSION >= 30)
-				bulletProgram = new ShaderProgram(Utils.readFully(getClass().getResourceAsStream(SHADERS_ROOT_PATH + "bullet3.0.vert")), Utils.readFully(getClass().getResourceAsStream(SHADERS_ROOT_PATH + "bullet3.0.frag")));
-			else
-				bulletProgram = new ShaderProgram(Utils.readFully(getClass().getResourceAsStream(SHADERS_ROOT_PATH + "bullet2.1.vert")), Utils.readFully(getClass().getResourceAsStream(SHADERS_ROOT_PATH + "bullet2.1.frag")));
-			
-			projectionMatrixUniform = bulletProgram.getUniformLocation("projectionMatrix");
-			modelViewMatrixUniform = bulletProgram.getUniformLocation("modelViewMatrix");
-			
-			if(GLUtils.get().VERSION >= 33) {
-				int bulletDataUniform = bulletProgram.getUniformBlockIndex("BulletData");
-				glUniformBlockBinding(bulletProgram.getProgram(), bulletDataUniform, 2);
-				
-				bulletDataVBO = glGenBuffers();
-				glBindBuffer(GL_UNIFORM_BUFFER, bulletDataVBO);
-				glBufferData(GL_UNIFORM_BUFFER, bulletDataBuffer.capacity() * 4, GL_STREAM_DRAW);
-				glBindBufferBase(GL_UNIFORM_BUFFER, 2, bulletDataVBO);
-				glBindBuffer(GL_UNIFORM_BUFFER, 0);
-			}
-			else
-				bulletDataUniform = bulletProgram.getUniformLocation("bulletData");
-		}
-		
-		public void render(Matrix4 projectionMatrix, MatrixStack modelViewMatrix, List<Bullet> bullets, FrustumCulling culling) {
-			glDepthMask(false);
-			
-			bulletProgram.begin();
-			
-			glUniformMatrix4(projectionMatrixUniform, false, projectionMatrix.toBuffer());
-			glUniformMatrix4(modelViewMatrixUniform, false, modelViewMatrix.getTop().toBuffer());
-			
-			bulletDataBuffer.clear();
-			
-			int bulletsDrawn = 0;
-			for(int a = 0; a < bullets.size() && bulletsDrawn < MAX_BULLET_COUNT; a++) {
-				Bullet b = bullets.get(a);
-				
-				// if(culling != null && !culling.isCubeInsideFrustum(b.getPosition(), Bullet.SIZE))
-				// continue;
-				
-				bulletsDrawn++;
-				
-				bulletDataBuffer.put(b.getPosition().toBuffer()).put(Bullet.SIZE);
-				bulletDataBuffer.put(b.getColor().toBuffer()).put(b.getAlpha());
-			}
-			
-			bulletDataBuffer.flip();
-			
-			if(GLUtils.get().VERSION >= 31) {
-				glBindBuffer(GL_UNIFORM_BUFFER, bulletDataVBO);
-				glBufferSubData(GL_UNIFORM_BUFFER, 0, bulletDataBuffer);
-				glBindBuffer(GL_UNIFORM_BUFFER, 0);
-			}
-			else
-				glUniform4(bulletDataUniform, bulletDataBuffer);
-			
-			if(HAS_VAO)
-				glBindVertexArray(vao);
-			else if(IS_MAC)
-				APPLEVertexArrayObject.glBindVertexArrayAPPLE(vao);
-			else
-				ARBVertexArrayObject.glBindVertexArray(vao);
-			
-			glDrawArrays(GL_TRIANGLES, 0, bulletsDrawn * 6);
-			
-			if(HAS_VAO)
-				glBindVertexArray(vao);
-			else if(IS_MAC)
-				APPLEVertexArrayObject.glBindVertexArrayAPPLE(vao);
-			else
-				ARBVertexArrayObject.glBindVertexArray(vao);
-			
-			bulletProgram.end();
-			
-			glDepthMask(true);
-		}
-	}
-}

File src/com/ra4king/fps/Camera.java

 	
 	public void update(long deltaTime) {
 		if(cameraUpdate != null)
-			cameraUpdate.update(deltaTime, this, projectionMatrix, position, orientation);
+			cameraUpdate.updateCamera(deltaTime, this, projectionMatrix, position, orientation);
 	}
 	
 	public CameraUpdate getCameraUpdate() {
 	}
 	
 	public static interface CameraUpdate {
-		public void update(long deltaTime, Camera camera, Matrix4 projectionMatrix, Vector3 position, Quaternion orientation);
+		public void updateCamera(long deltaTime, Camera camera, Matrix4 projectionMatrix, Vector3 position, Quaternion orientation);
 	}
 }

File src/com/ra4king/fps/Chunk.java

-package com.ra4king.fps;
-
-import static org.lwjgl.opengl.GL11.*;
-import static org.lwjgl.opengl.GL15.*;
-import static org.lwjgl.opengl.GL20.*;
-import static org.lwjgl.opengl.GL30.*;
-import static org.lwjgl.opengl.GL31.*;
-
-import java.nio.FloatBuffer;
-
-import org.lwjgl.BufferUtils;
-
-import com.ra4king.opengl.util.math.Vector3;
-
-/**
- * @author ra4king
- */
-public class Chunk {
-	public enum BlockType {
-		DIRT
-	}
-	
-	private final BlockType[] blocks;
-	private final Vector3 corner;
-	
-	private int cubeCount;
-	
-	private final ChunkRenderer renderer;
-	
-	public static final int CUBES = GLUtils.get().VERSION >= 33 ? 16 : 8;
-	public static final float CUBE_SIZE = 2;
-	public static final float SPACING = 7;
-	
-	public Chunk(Vector3 corner, boolean random) {
-		this.corner = corner.copy();
-		
-		blocks = new BlockType[CUBES * CUBES * CUBES];
-		
-		if(random)
-			initializeRandomly();
-
-		renderer = new ChunkRenderer();
-	}
-	
-	public void initializeRandomly() {
-		cubeCount = (int)(Math.random() * blocks.length / 10);
-		
-		for(int a = 0; a < cubeCount; a++) {
-			int ix;
-			do {
-				ix = (int)(Math.random() * blocks.length);
-				
-				if(blocks[ix] != null) {
-					ix = -1;
-					continue;
-				}
-				
-				blocks[ix] = BlockType.DIRT;
-			} while(ix == -1);
-		}
-	}
-	
-	public int getCubeCount() {
-		return cubeCount;
-	}
-	
-	public Vector3 getCorner() {
-		return corner;
-	}
-	
-	public BlockType get(int x, int y, int z) {
-		return blocks[x * CUBES * CUBES + y * CUBES + z];
-	}
-	
-	public void add(BlockType block, int x, int y, int z) {
-		if(x >= CUBES || x < 0 ||
-				y >= CUBES || y < 0 ||
-				z >= CUBES || z < 0)
-			throw new IllegalArgumentException("Invalid block position.");
-		
-		blocks[x * CUBES * CUBES + y * CUBES + z] = block;
-	}
-	
-	public void update(long deltaTime) {
-		
-	}
-	
-	public void render() {
-		renderer.render();
-	}
-	
-	private class ChunkRenderer {
-		private int dataVBO;
-		
-		private FloatBuffer buffer;
-		
-		public ChunkRenderer() {
-			buffer = BufferUtils.createFloatBuffer(CUBES * CUBES * CUBES);
-			
-			if(GLUtils.get().VERSION >= 33) {
-				dataVBO = glGenBuffers();
-				glBindBuffer(GL_UNIFORM_BUFFER, dataVBO);
-				glBufferData(GL_UNIFORM_BUFFER, buffer.capacity() * 4, GL_STATIC_DRAW);
-				glBindBuffer(GL_UNIFORM_BUFFER, 0);
-			}
-			
-			updateVBO();
-		}
-		
-		public void updateVBO() {
-			buffer.clear();
-			
-			for(int x = 0; x < CUBES; x++) {
-				for(int y = 0; y < CUBES; y++) {
-					for(int z = 0; z < CUBES; z++) {
-						if(get(x, y, z) == null)
-							continue;
-						
-						buffer.put(corner.x() + x * SPACING + CUBE_SIZE / 2).put(corner.y() + y * SPACING + CUBE_SIZE / 2).put(corner.z() - z * SPACING - CUBE_SIZE / 2).put(CUBE_SIZE);
-					}
-				}
-			}
-			
-			buffer.flip();
-			
-			glBindBuffer(GL_UNIFORM_BUFFER, dataVBO);
-			glBufferSubData(GL_UNIFORM_BUFFER, 0, buffer);
-			glBindBuffer(GL_UNIFORM_BUFFER, 0);
-		}
-		
-		public void render() {
-			if(GLUtils.get().VERSION >= 31)
-				glBindBufferBase(GL_UNIFORM_BUFFER, 3, dataVBO);
-			else
-				glUniform1(FPS.cubesUniform, buffer);
-			
-			glDrawArrays(GL_TRIANGLES, 0, getCubeCount() * 36);
-		}
-	}
-}

File src/com/ra4king/fps/ChunkManager.java

-package com.ra4king.fps;
-
-import java.util.HashSet;
-
-import com.ra4king.fps.Chunk.BlockType;
-import com.ra4king.fps.FPS.FrustumCulling;
-import com.ra4king.opengl.util.math.Vector3;
-
-/**
- * @author ra4king
- */
-public class ChunkManager {
-	private HashSet<Chunk> chunks;
-	
-	public static final int CHUNKS_SIDE = 20;
-	
-	public ChunkManager() {
-		chunks = new HashSet<>();
-		
-		int totalCubes = 0;
-		
-		Vector3 temp = new Vector3();
-		for(int x = 0; x < CHUNKS_SIDE; x++)
-			for(int y = 0; y < CHUNKS_SIDE; y++)
-				for(int z = 0; z < CHUNKS_SIDE; z++) {
-					Chunk chunk = new Chunk(temp.set(x * Chunk.CUBES * Chunk.SPACING, y * Chunk.CUBES * Chunk.SPACING, -z * Chunk.CUBES * Chunk.SPACING), true);
-					chunks.add(chunk);
-					
-					totalCubes += chunk.getCubeCount();
-				}
-		
-		System.out.println("Total cubes: " + totalCubes);
-	}
-	
-	private final Vector3 temp = new Vector3();
-	private final Vector3 temp2 = new Vector3();
-	
-	public BlockType getBlock(Vector3 v, float radius) {
-		float distance = Chunk.CUBE_SIZE / 2 + radius;
-		
-		final float d = Chunk.CUBES * Chunk.SPACING;
-		
-		if(v.x() < -distance || v.x() > CHUNKS_SIDE * d + distance ||
-				v.y() < -distance || v.y() > CHUNKS_SIDE * d + distance ||
-				v.z() > distance || v.z() < -CHUNKS_SIDE * d - distance) {
-			return null;
-		}
-		
-		float ix = (int)(v.x() / d) * d;
-		float iy = (int)(v.y() / d) * d;
-		float iz = (int)(v.z() / d) * d;
-		temp.set(ix, iy, iz);
-		
-		int px = (int)(((v.x() + distance) % d) / Chunk.SPACING);
-		int py = (int)(((v.y() + distance) % d) / Chunk.SPACING);
-		int pz = (int)(((-v.z() + distance) % d) / Chunk.SPACING);
-		
-		for(Chunk chunk : chunks) {
-			if(chunk.getCorner().equals(temp)) {
-				BlockType block = chunk.get(px, py, pz);
-				
-				if(block == null)
-					return null;
-				
-				Vector3 vBlock = temp.set(chunk.getCorner()).add(temp2.set(px, py, -pz).mult(Chunk.SPACING));
-				
-				if(vBlock.sub(v).length() > distance)
-					return null;
-				
-				return block;
-			}
-		}
-		
-		return null;
-	}
-	
-	public void update(long deltaTime) {
-		// BLAH
-	}
-	
-	private int vao = -1;
-	
-	public void render(FrustumCulling culling) {
-		if(vao == -1)
-			vao = GLUtils.get().glGenVertexArrays();
-		
-		GLUtils.get().glBindVertexArray(vao);
-		
-		for(Chunk chunk : chunks)
-			if(culling.isCubeInsideFrustum(chunk.getCorner(), Chunk.CUBES * Chunk.SPACING * Chunk.CUBE_SIZE))
-				chunk.render();
-		
-		GLUtils.get().glBindVertexArray(0);
-	}
-}

File src/com/ra4king/fps/FPS.java

 package com.ra4king.fps;
 
 import static org.lwjgl.opengl.GL11.*;
-import static org.lwjgl.opengl.GL15.*;
-import static org.lwjgl.opengl.GL20.*;
-import static org.lwjgl.opengl.GL30.*;
-import static org.lwjgl.opengl.GL31.*;
-import static org.lwjgl.opengl.GL32.*;
 
-import java.nio.FloatBuffer;
-
-import org.lwjgl.BufferUtils;
 import org.lwjgl.input.Keyboard;
 import org.lwjgl.input.Mouse;
 import org.lwjgl.opengl.PixelFormat;
 
-import com.ra4king.fps.Camera.CameraUpdate;
+import com.ra4king.fps.renderers.WorldRenderer;
+import com.ra4king.fps.world.World;
 import com.ra4king.opengl.util.GLProgram;
-import com.ra4king.opengl.util.ShaderProgram;
-import com.ra4king.opengl.util.Utils;
-import com.ra4king.opengl.util.math.Matrix4;
-import com.ra4king.opengl.util.math.MatrixStack;
-import com.ra4king.opengl.util.math.Quaternion;
-import com.ra4king.opengl.util.math.Vector3;
-import com.ra4king.opengl.util.math.Vector4;
 
 /**
  * @author ra4king
 		new FPS().run(new PixelFormat(16, 0, 8, 0, 4));
 	}
 	
-	private ShaderProgram program;
-	private int projectionMatrixUniform;
-	private int modelViewMatrixUniform;
-	
-	private int lightsUniformBufferVBO;
-	private FloatBuffer lightsBuffer;
-	
-	private boolean isPaused;
-	
-	private Camera camera;
-	
-	private FrustumCulling culling;
-	private ChunkManager chunkManager;
-	private BulletManager bulletManager;
-	
-	public static final String SHADERS_ROOT_PATH = "/res/shaders/";
+	private World world;
+	private WorldRenderer worldRenderer;
 	
 	// private Fractal fractal;
 	
 		
 		setFPS(0);
 		
-		Mouse.setGrabbed(true);
+		// Mouse.setGrabbed(true);
 		
-		glClearColor(0, 0, 0, 0);// 0.4f, 0.6f, 0.9f, 0f);
-		
-		glEnable(GL_DEPTH_TEST);
-		glDepthRange(0, 1);
-		
-		glDepthMask(true);
-		
-		glEnable(GL_CULL_FACE);
-		glCullFace(GL_BACK);
-		glFrontFace(GL_CW);
-		
-		glEnable(GL_BLEND);
-		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-		
-		if(GLUtils.get().VERSION >= 32)
-			glEnable(GL_DEPTH_CLAMP);
-		
-		camera = new Camera(60, 1, 5000);
-		camera.setCameraUpdate(new CameraUpdate() {
-			private float deltaTimeBuffer;
-			private final Vector3 delta = new Vector3();
-			private final Quaternion inverse = new Quaternion();
-			
-			private long mouseCooldown;
-			
-			@Override
-			public void update(long deltaTime, Camera camera, final Matrix4 projectionMatrix, final Vector3 position, final Quaternion orientation) {
-				if(Keyboard.isKeyDown(Keyboard.KEY_R))
-					reset();
-				
-				deltaTimeBuffer += deltaTime;
-				
-				if(deltaTimeBuffer >= 1e9 / 120) {
-					final float speed = (Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) | Keyboard.isKeyDown(Keyboard.KEY_RSHIFT) ? 20 : 150) * deltaTimeBuffer / (float)1e9;
-					final float rotSpeed = (2f / 15f) * speed;
-					
-					deltaTimeBuffer = 0;
-					
-					if(Mouse.isGrabbed()) {
-						int dy = Mouse.getDY();
-						if(dy != 0)
-							orientation.set(Utils.angleAxisDeg(-dy * rotSpeed, Vector3.RIGHT).mult(orientation));
-						
-						int dx = Mouse.getDX();
-						if(dx != 0)
-							orientation.set(Utils.angleAxisDeg(dx * rotSpeed, Vector3.UP).mult(orientation));
-						
-						// if(dx != 0 || dy != 0)
-						// viewChanged = true;
-					}
-					
-					if(Keyboard.isKeyDown(Keyboard.KEY_E)) {
-						orientation.set(Utils.angleAxisDeg(-2 * rotSpeed, Vector3.FORWARD).mult(orientation));
-						// viewChanged = true;
-					}
-					if(Keyboard.isKeyDown(Keyboard.KEY_Q)) {
-						orientation.set(Utils.angleAxisDeg(2 * rotSpeed, Vector3.FORWARD).mult(orientation));
-						// viewChanged = true;
-					}
-					
-					orientation.normalize();
-					
-					inverse.set(orientation).inverse();
-					
-					delta.set(0, 0, 0);
-					
-					if(Keyboard.isKeyDown(Keyboard.KEY_W))
-						delta.z(delta.z() + speed);
-					if(Keyboard.isKeyDown(Keyboard.KEY_S))
-						delta.z(-speed);
-					
-					if(Keyboard.isKeyDown(Keyboard.KEY_D))
-						delta.x(-speed);
-					if(Keyboard.isKeyDown(Keyboard.KEY_A))
-						delta.x(delta.x() + speed);
-					
-					if(Keyboard.isKeyDown(Keyboard.KEY_SPACE))
-						delta.y(-speed);
-					if(Keyboard.isKeyDown(Keyboard.KEY_LCONTROL))
-						delta.y(delta.y() + speed);
-					
-					// if(delta.length() != 0)
-					// viewChanged = true;
-					
-					position.add(inverse.mult(delta));
-				}
-				
-				if((Mouse.isButtonDown(0) || Keyboard.isKeyDown(Keyboard.KEY_C)) && (System.nanoTime() - mouseCooldown) > (long)3e8) {
-					int bulletSpeed = 400;
-					
-					bulletManager.addBullet(new Bullet(position.copy().mult(-1).add(inverse.mult(rightBullet)), inverse.mult(Vector3.FORWARD).mult(bulletSpeed), BulletManager.BULLET_LIFE));
-					bulletManager.addBullet(new Bullet(position.copy().mult(-1).add(inverse.mult(leftBullet)), inverse.mult(Vector3.FORWARD).mult(bulletSpeed), BulletManager.BULLET_LIFE));
-					mouseCooldown = System.nanoTime();
-				}
-			}
-		});
-		
-		reload();
-	}
-	
-	private void reset() {
-		// float mid = ChunkManager.CHUNKS_SIDE * Chunk.CUBES * Chunk.SPACING / 2;
-		camera.getPosition().set(0, 0, 0);// rsrmid, mid, -mid).mult(-1);
-		camera.getOrientation().reset();
+		world = new World();
+		worldRenderer = new WorldRenderer(world);
 	}
 	
 	@Override
 	public void resized() {
 		super.resized();
 		
-		camera.setWindowSize(getWidth(), getHeight());
+		world.resized();
 	}
 	
-	private int mainLightPositionUniform, mainDiffuseColorUniform, mainAmbientColorUniform;
-	private int numberOfLightsUniform;
-	private int lightPositionsUniform, lightColorsUniform;
-	public static int cubesUniform;
-	
-	private FloatBuffer lightsColorBuffer;
-	
-	private void reload() {
-		if(GLUtils.get().VERSION >= 33)
-			program = new ShaderProgram(Utils.readFully(getClass().getResourceAsStream(SHADERS_ROOT_PATH + "fps.vert")), Utils.readFully(getClass().getResourceAsStream(SHADERS_ROOT_PATH + "fps.frag")));
-		else if(GLUtils.get().VERSION >= 30)
-			program = new ShaderProgram(Utils.readFully(getClass().getResourceAsStream(SHADERS_ROOT_PATH + "fps3.0.vert")), Utils.readFully(getClass().getResourceAsStream(SHADERS_ROOT_PATH + "fps3.0.frag")));
-		else {
-			throw new RuntimeException("2.1 and below not supported.");
-			
-			// program = new ShaderProgram(Utils.readFully(getClass().getResourceAsStream(SHADERS_ROOT_PATH + "fps2.1.vert")), Utils.readFully(getClass().getResourceAsStream(SHADERS_ROOT_PATH + "fps2.1.frag")));
-		}
-		
-		lightsBuffer = BufferUtils.createFloatBuffer((BulletManager.MAX_BULLET_COUNT * 2 + 4) * 4 * 4);
-		
-		if(GLUtils.get().VERSION >= 33) {
-			int lightsBlockIndex = program.getUniformBlockIndex("Lights");
-			glUniformBlockBinding(program.getProgram(), lightsBlockIndex, 1);
-			
-			int cubeDataUniform = glGetUniformBlockIndex(program.getProgram(), "CubeData");
-			glUniformBlockBinding(program.getProgram(), cubeDataUniform, 3);
-			
-			lightsUniformBufferVBO = glGenBuffers();
-			glBindBuffer(GL_UNIFORM_BUFFER, lightsUniformBufferVBO);
-			glBufferData(GL_UNIFORM_BUFFER, lightsBuffer.capacity() * 4, GL_STREAM_DRAW);
-			glBindBufferBase(GL_UNIFORM_BUFFER, 1, lightsUniformBufferVBO);
-			glBindBuffer(GL_UNIFORM_BUFFER, 0);
-		}
-		else {
-			cubesUniform = program.getUniformLocation("cubes");
-			
-			mainLightPositionUniform = program.getUniformLocation("mainLightPosition");
-			mainDiffuseColorUniform = program.getUniformLocation("mainDiffuseColor");
-			mainAmbientColorUniform = program.getUniformLocation("mainAmbientColor");
-			numberOfLightsUniform = program.getUniformLocation("numberOfLights");
-			lightPositionsUniform = program.getUniformLocation("lightPositions");
-			lightColorsUniform = program.getUniformLocation("lightColors");
-			
-			lightsColorBuffer = BufferUtils.createFloatBuffer(lightsBuffer.capacity());
-		}
-		
-		projectionMatrixUniform = program.getUniformLocation("projectionMatrix");
-		modelViewMatrixUniform = program.getUniformLocation("modelViewMatrix");
-		
-		culling = new FrustumCulling();
-		
-		chunkManager = new ChunkManager();
-		bulletManager = new BulletManager(chunkManager);
-		
-		// fractal = new Fractal();
-	}
-	
-	private long secondTimeBuffer;
-	
-	private final Vector3 delta = new Vector3();
-	
-	private final Vector3 rightBullet = new Vector3(2, -1, -3);
-	private final Vector3 leftBullet = new Vector3(-2, -1, -3);
-	
 	@Override
 	public void update(long deltaTime) {
-		secondTimeBuffer += deltaTime;
-		
-		if(secondTimeBuffer >= 1e9) {
-			// System.out.println(fractal.getDepth() + " " + fractal.getTotal());
-			secondTimeBuffer -= 1e9;
-		}
-		
-		camera.update(deltaTime);
-		
-		// fractal.update(deltaTime);
-		
-		if(!isPaused)
-			bulletManager.update(deltaTime);
+		world.update(deltaTime);
+		worldRenderer.update(deltaTime);
 	}
 	
 	@Override
 		if(key == Keyboard.KEY_ESCAPE)
 			Mouse.setGrabbed(!Mouse.isGrabbed());
 		
-		if(key == Keyboard.KEY_T) {
-			reload();
-			resized();
-		}
-		
-		if(key == Keyboard.KEY_P)
-			isPaused = !isPaused;
+		world.keyPressed(key, c);
 	}
 	
 	@Override
 		return false;
 	}
 	
-	private final Vector4 mainDiffuseColor = new Vector4(1f, 1f, 1f, 1);
-	private final Vector4 mainAmbientColor = new Vector4(0.01f, 0.01f, 0.01f, 1);
-	
-	private final Bullet aim = new Bullet(new Vector3(), new Vector3(), Long.MAX_VALUE, false, new Vector3(1));
-	
-	private final Matrix4 viewMatrix = new Matrix4(), projectionViewMatrix = new Matrix4();
-	
 	@Override
 	public void render() {
 		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 		
-		program.begin();
-		
-		glUniformMatrix4(projectionMatrixUniform, false, camera.getProjectionMatrix().toBuffer());
-		
-		camera.getOrientation().toMatrix(viewMatrix).translate(camera.getPosition());
-		
-		culling.setupPlanes(projectionViewMatrix.set(camera.getProjectionMatrix()).mult(viewMatrix));
-		
-		final float mainK = 0.001f;
-		
-		lightsBuffer.clear();
-		
-		if(GLUtils.get().VERSION >= 33)
-			lightsBuffer.position(4 * 4);
-		else
-			lightsColorBuffer.clear();
-		
-		int bulletCount = bulletManager.getBulletLightData(viewMatrix, lightsBuffer, GLUtils.get().VERSION >= 33 ? lightsBuffer : lightsColorBuffer);
-		
-		lightsBuffer.flip();
-		
-		Vector3 position = camera.getPosition();
-		if(GLUtils.get().VERSION >= 33) {
-			lightsBuffer.put(position.toBuffer());
-			lightsBuffer.put(mainK);
-			lightsBuffer.put(mainDiffuseColor.toBuffer());
-			lightsBuffer.put(mainAmbientColor.toBuffer());
-			
-			lightsBuffer.put(bulletCount).put(0).put(0).put(0).rewind();
-			
-			glBindBuffer(GL_UNIFORM_BUFFER, lightsUniformBufferVBO);
-			glBufferSubData(GL_UNIFORM_BUFFER, 0, lightsBuffer);
-			glBindBuffer(GL_UNIFORM_BUFFER, 0);
-		}
-		else {
-			glUniform4f(mainLightPositionUniform, position.x(), position.y(), position.z(), mainK);
-			glUniform4(mainDiffuseColorUniform, mainDiffuseColor.toBuffer());
-			glUniform4(mainAmbientColorUniform, mainAmbientColor.toBuffer());
-			glUniform1f(numberOfLightsUniform, bulletCount);
-			
-			lightsColorBuffer.flip();
-			
-			glUniform4(lightPositionsUniform, lightsBuffer);
-			glUniform4(lightColorsUniform, lightsColorBuffer);
-		}
-		
-		glUniformMatrix4(modelViewMatrixUniform, false, viewMatrix.toBuffer());
-		
-		// fractal.render(new MatrixStack());
-		
-		chunkManager.render(culling);// viewChanged);
-		program.end();
-		
-		bulletManager.render(camera.getProjectionMatrix(), new MatrixStack().setTop(viewMatrix), culling);
-		
-		glDepthMask(false);
-		
-		float oldSize = Bullet.SIZE;
-		Bullet.SIZE = 4;
-		
-		glDisable(GL_DEPTH_TEST);
-		bulletManager.render(new Matrix4().clearToOrtho(-getWidth() / 2, getWidth() / 2, -getHeight() / 2, getHeight() / 2, -1, 1), new MatrixStack(), null, aim);
-		glEnable(GL_DEPTH_TEST);
-		
-		Bullet.SIZE = oldSize;
-		
-		glDepthMask(true);
-		
-		// viewChanged = false;
-	}
-	
-	public static class FrustumCulling {
-		private enum Plane {
-			LEFT, RIGHT, BOTTOM, TOP, NEAR, FAR;
-			
-			private Vector4 plane;
-			
-			public static final Plane[] values = values();
-			
-			private final Vector3 temp = new Vector3();
-			
-			public float distanceFromPoint(Vector3 point) {
-				return temp.set(plane).dot(point) + plane.w();
-			}
-		}
-		
-		public void setupPlanes(Matrix4 matrix) {
-			Plane.LEFT.plane = getPlane(matrix, 1);
-			Plane.RIGHT.plane = getPlane(matrix, -1);
-			Plane.BOTTOM.plane = getPlane(matrix, 2);
-			Plane.TOP.plane = getPlane(matrix, -2);
-			Plane.NEAR.plane = getPlane(matrix, 3);
-			Plane.FAR.plane = getPlane(matrix, -3);
-		}
-		
-		private Vector4 getPlane(Matrix4 matrix, int row) {
-			int scale = row < 0 ? -1 : 1;
-			row = Math.abs(row) - 1;
-			
-			return new Vector4(
-					matrix.get(3) + scale * matrix.get(row),
-					matrix.get(7) + scale * matrix.get(row + 4),
-					matrix.get(11) + scale * matrix.get(row + 8),
-					matrix.get(15) + scale * matrix.get(row + 12)).normalize();
-		}
-		
-		private final Vector3 temp = new Vector3();
-		
-		public boolean isCubeInsideFrustum(Vector3 center, float sideLength) {
-			for(Plane p : Plane.values) {
-				float d = sideLength / 2;
-				
-				boolean isIn;
-				
-				isIn = p.distanceFromPoint(temp.set(center).add(d, d, d)) >= 0;
-				isIn |= p.distanceFromPoint(temp.set(center).add(d, d, -d)) >= 0;
-				isIn |= p.distanceFromPoint(temp.set(center).add(d, -d, d)) >= 0;
-				isIn |= p.distanceFromPoint(temp.set(center).add(d, -d, -d)) >= 0;
-				isIn |= p.distanceFromPoint(temp.set(center).add(-d, d, d)) >= 0;
-				isIn |= p.distanceFromPoint(temp.set(center).add(-d, d, -d)) >= 0;
-				isIn |= p.distanceFromPoint(temp.set(center).add(-d, -d, d)) >= 0;
-				isIn |= p.distanceFromPoint(temp.set(center).add(-d, -d, -d)) >= 0;
-				
-				if(!isIn)
-					return false;
-			}
-			
-			return true;
-		}
-		
-		public boolean isPointInsideFrustum(Vector3 point) {
-			boolean isIn = true;
-			
-			for(Plane p : Plane.values)
-				isIn &= p.distanceFromPoint(point) > 0;
-			
-			return isIn;
-		}
+		worldRenderer.render();
 	}
 }

File src/com/ra4king/fps/Fractal.java

-package com.ra4king.fps;
-
-import static org.lwjgl.opengl.GL11.*;
-import static org.lwjgl.opengl.GL15.*;
-import static org.lwjgl.opengl.GL20.*;
-import static org.lwjgl.opengl.GL30.*;
-
-import java.nio.FloatBuffer;
-
-import org.lwjgl.BufferUtils;
-import org.lwjgl.opengl.APPLEVertexArrayObject;
-import org.lwjgl.opengl.ARBVertexArrayObject;
-import org.lwjgl.opengl.GLContext;
-
-import com.ra4king.opengl.util.loader.PolygonLoader;
-import com.ra4king.opengl.util.math.MatrixStack;
-import com.ra4king.opengl.util.math.Vector2;
-import com.ra4king.opengl.util.math.Vector3;
-
-/**
- * @author ra4king
- */
-public class Fractal {
-	private CubeRenderer renderer;
-	
-	private int depth, change = 1;
-	private long timePassed;
-	
-	private final float SPLIT_TIME = (float)1e9;
-	private final int MAX_DEPTH = 21;
-	
-	public Fractal() {
-		renderer = new CubeRenderer();
-	}
-	
-	public int getDepth() {
-		return depth;
-	}
-	
-	public int getTotal() {
-		return (int)(Math.pow(2, depth + 1) - 1);
-	}
-	
-	public void update(long deltaTime) {
-		timePassed += deltaTime;
-		
-		if(timePassed >= SPLIT_TIME) {
-			timePassed -= SPLIT_TIME;
-			
-			if(depth <= MAX_DEPTH && depth >= 0)
-				depth += change;
-			
-			if(depth == MAX_DEPTH || depth == 0)
-				change *= -1;
-		}
-	}
-	
-	public void render(MatrixStack matrixStack) {
-		renderer.render(matrixStack);
-	}
-	
-	private class CubeRenderer {
-		private FloatBuffer buffer;
-		private int cubeVAO, dataVBO;
-		
-		private final boolean IS_MAC = System.getProperty("os.name").toLowerCase().contains("mac");
-		private final boolean HAS_VAO = GLContext.getCapabilities().OpenGL30;
-		
-		public CubeRenderer() {
-			buffer = BufferUtils.createFloatBuffer((int)((Math.pow(2, MAX_DEPTH + 1) - 1) * 4 * 6 * 3));
-			
-			dataVBO = glGenBuffers();
-			glBindBuffer(GL_ARRAY_BUFFER, dataVBO);
-			glBufferData(GL_ARRAY_BUFFER, buffer.capacity() * 4, GL_STREAM_DRAW);
-			
-			cubeVAO = HAS_VAO ? glGenVertexArrays() : (IS_MAC ? APPLEVertexArrayObject.glGenVertexArraysAPPLE() : ARBVertexArrayObject.glGenVertexArrays());
-			if(HAS_VAO)
-				glBindVertexArray(cubeVAO);
-			else if(IS_MAC)
-				APPLEVertexArrayObject.glBindVertexArrayAPPLE(cubeVAO);
-			else
-				ARBVertexArrayObject.glBindVertexArray(cubeVAO);
-			
-			glEnableVertexAttribArray(0);
-			glVertexAttribPointer(0, 3, GL_FLOAT, false, 6 * 4, 0);
-			glEnableVertexAttribArray(1);
-			glVertexAttribPointer(1, 3, GL_FLOAT, false, 6 * 4, 3 * 4);
-			
-			if(HAS_VAO)
-				glBindVertexArray(0);
-			else if(IS_MAC)
-				APPLEVertexArrayObject.glBindVertexArrayAPPLE(0);
-			else
-				ARBVertexArrayObject.glBindVertexArray(0);
-			
-			glBindBuffer(GL_ARRAY_BUFFER, 0);
-		}
-		
-		private final double pow = 1.3;
-		private final float SIZE_CONSTANT = 3.7f;
-		
-		private void render(MatrixStack matrixStack, float angle, int curDepth, FloatBuffer buffer) {
-			float radius = (float)Math.pow(pow, getDepth() - curDepth + change * timePassed / SPLIT_TIME) / SIZE_CONSTANT;
-			
-			matrixStack.getTop().translate(0, radius, 0);
-			try {
-				buffer.put(PolygonLoader.loadPlane(new Vector2(0.1f, 2 * radius), new Vector3(0, 0, 0), true, false, matrixStack.getTop()));
-			} catch(Exception exc) {
-				System.out.println(getDepth() + " " + getTotal() + " " + (getTotal() * 72) + " " + buffer.capacity());
-				throw exc;
-			}
-			matrixStack.getTop().translate(0, radius, 0);
-			
-			if(curDepth < getDepth() && curDepth < MAX_DEPTH) {
-				matrixStack.pushMatrix();
-				matrixStack.getTop().rotate((float)(-Math.PI / 2 + angle), 0, 0, 1);
-				render(matrixStack, angle, curDepth + 1, buffer);
-				matrixStack.popMatrix();
-				
-				matrixStack.pushMatrix();
-				matrixStack.getTop().rotate(angle, 0, 0, 1);
-				render(matrixStack, angle, curDepth + 1, buffer);
-				matrixStack.popMatrix();
-			}
-		}
-		
-		public void render(MatrixStack matrixStack) {
-			matrixStack.getTop().translate(0, -20, Math.min(-40 * getDepth() - 40 * change * timePassed / SPLIT_TIME, -40));
-			
-			buffer.clear();
-			
-			float angle = (float)Math.toRadians((getDepth() + change * timePassed / SPLIT_TIME) * 45f / MAX_DEPTH);
-			render(matrixStack, angle, 0, buffer);
-			
-			buffer.flip();
-			
-			glBindBuffer(GL_ARRAY_BUFFER, dataVBO);
-			glBufferSubData(GL_ARRAY_BUFFER, 0, buffer);
-			glBindBuffer(GL_ARRAY_BUFFER, 0);
-			
-			if(HAS_VAO)
-				glBindVertexArray(cubeVAO);
-			else if(IS_MAC)
-				APPLEVertexArrayObject.glBindVertexArrayAPPLE(cubeVAO);
-			else
-				ARBVertexArrayObject.glBindVertexArray(cubeVAO);
-			
-			glDrawArrays(GL_TRIANGLES, 0, buffer.limit() / 6);
-			
-			if(HAS_VAO)
-				glBindVertexArray(0);
-			else if(IS_MAC)
-				APPLEVertexArrayObject.glBindVertexArrayAPPLE(0);
-			else
-				ARBVertexArrayObject.glBindVertexArray(0);
-		}
-	}
-}

File src/com/ra4king/fps/GLUtils.java

 
 import org.lwjgl.opengl.APPLEVertexArrayObject;
 import org.lwjgl.opengl.ARBVertexArrayObject;
+import org.lwjgl.opengl.Display;
 import org.lwjgl.opengl.GL30;
 import org.lwjgl.opengl.GLContext;
 
+import com.ra4king.opengl.util.math.Matrix4;
+import com.ra4king.opengl.util.math.Vector3;
+import com.ra4king.opengl.util.math.Vector4;
+
 /**
  * @author ra4king
  */
 	public final boolean HAS_VAO;
 	public final boolean HAS_ARB_VAO;
 	
+	public int CUBES_UNIFORM;
+	
+	public final String SHADERS_ROOT_PATH = "/res/shaders/";
+	
 	private GLUtils() {
 		VERSION = GLContext.getCapabilities().OpenGL33 ? 33 : GLContext.getCapabilities().OpenGL30 ? 30 : GLContext.getCapabilities().OpenGL21 ? 21 : 0;
 		
 		return glUtils;
 	}
 	
+	public int getWidth() {
+		return Display.getWidth();
+	}
+	
+	public int getHeight() {
+		return Display.getHeight();
+	}
+	
 	public void glBindVertexArray(int vao) {
 		if(HAS_VAO)
 			GL30.glBindVertexArray(vao);
 		else
 			throw new UnsupportedOperationException("VAOs not supported on this system.");
 	}
+	
+	public static class FrustumCulling {
+		private enum Plane {
+			LEFT, RIGHT, BOTTOM, TOP, NEAR, FAR;
+			
+			private Vector4 plane;
+			
+			public static final Plane[] values = values();
+			
+			private final Vector3 temp = new Vector3();
+			
+			public float distanceFromPoint(Vector3 point) {
+				return temp.set(plane).dot(point) + plane.w();
+			}
+		}
+		
+		public void setupPlanes(Matrix4 matrix) {
+			Plane.LEFT.plane = getPlane(matrix, 1);
+			Plane.RIGHT.plane = getPlane(matrix, -1);
+			Plane.BOTTOM.plane = getPlane(matrix, 2);
+			Plane.TOP.plane = getPlane(matrix, -2);
+			Plane.NEAR.plane = getPlane(matrix, 3);
+			Plane.FAR.plane = getPlane(matrix, -3);
+		}
+		
+		private Vector4 getPlane(Matrix4 matrix, int row) {
+			int scale = row < 0 ? -1 : 1;
+			row = Math.abs(row) - 1;
+			
+			return new Vector4(
+					matrix.get(3) + scale * matrix.get(row),
+					matrix.get(7) + scale * matrix.get(row + 4),
+					matrix.get(11) + scale * matrix.get(row + 8),
+					matrix.get(15) + scale * matrix.get(row + 12)).normalize();
+		}
+		
+		private final Vector3 temp = new Vector3();
+		
+		public boolean isCubeInsideFrustum(Vector3 center, float sideLength) {
+			for(Plane p : Plane.values) {
+				float d = sideLength / 2;
+				
+				boolean isIn;
+				
+				isIn = p.distanceFromPoint(temp.set(center).add(d, d, d)) >= 0;
+				isIn |= p.distanceFromPoint(temp.set(center).add(d, d, -d)) >= 0;
+				isIn |= p.distanceFromPoint(temp.set(center).add(d, -d, d)) >= 0;
+				isIn |= p.distanceFromPoint(temp.set(center).add(d, -d, -d)) >= 0;
+				isIn |= p.distanceFromPoint(temp.set(center).add(-d, d, d)) >= 0;
+				isIn |= p.distanceFromPoint(temp.set(center).add(-d, d, -d)) >= 0;
+				isIn |= p.distanceFromPoint(temp.set(center).add(-d, -d, d)) >= 0;
+				isIn |= p.distanceFromPoint(temp.set(center).add(-d, -d, -d)) >= 0;
+				
+				if(!isIn)
+					return false;
+			}
+			
+			return true;
+		}
+		
+		public boolean isPointInsideFrustum(Vector3 point) {
+			boolean isIn = true;
+			
+			for(Plane p : Plane.values)
+				isIn &= p.distanceFromPoint(point) >= 0;
+			
+			return isIn;
+		}
+	}
 }

File src/com/ra4king/fps/actors/Bullet.java

+package com.ra4king.fps.actors;
+
+import com.ra4king.opengl.util.math.Vector3;
+
+/**
+ * @author ra4king
+ */
+public class Bullet {
+	private final Vector3 position, velocity;
+	private float range;
+	private float size;
+	
+	private Vector3 color;
+	
+	private boolean isSolid;
+	
+	private final long life;
+	private long alive;
+	
+	public Bullet(Vector3 position, Vector3 velocity, float size, float range) {
+		this(position, velocity, size, range, (long)5e9);
+	}
+	
+	public Bullet(Vector3 position, Vector3 velocity, float size, float range, long lifeTime) {
+		this(position, velocity, size, range, lifeTime, true);
+	}
+	
+	private static final Vector3[] colors = { new Vector3(1, 20f / 255f, 147f / 255f), new Vector3(1, 0, 0), new Vector3(0, 250f / 255f, 154f / 255f), new Vector3(0, 191f / 255f, 1) };
+	
+	public Bullet(Vector3 position, Vector3 velocity, float size, float range, long lifeTime, boolean isSolid) {
+		this(position, velocity, size, range, lifeTime, isSolid, colors[(int)(Math.random() * colors.length)]);
+	}
+	
+	public Bullet(Vector3 position, Vector3 velocity, float size, float range, long lifeTime, boolean isSolid, Vector3 color) {
+		this.position = position.copy();
+		this.velocity = velocity.copy();
+		this.size = size;
+		this.range = range;
+		life = lifeTime;
+		
+		this.isSolid = isSolid;
+		
+		this.color = color.copy();
+	}
+	
+	public boolean isAlive() {
+		return alive < life;
+	}
+	
+	public boolean isSolid() {
+		return isSolid;
+	}
+	
+	public float getAlpha() {
+		return (float)(life - alive) / life;
+	}
+	
+	public Vector3 getPosition() {
+		return position;
+	}
+	
+	public Vector3 getVelocity() {
+		return velocity;
+	}
+	
+	public float getSize() {
+		return size;
+	}
+	
+	public Vector3 getColor() {
+		return color;
+	}
+	
+	public float getRange() {
+		return range;
+	}
+	
+	private final Vector3 temp = new Vector3();
+	
+	public void update(long deltaTime) {
+		alive += deltaTime;
+		
+		position.add(temp.set(velocity).mult(deltaTime / (float)1e9));
+	}
+	
+	@Override
+	public String toString() {
+		return "Bullet @ " + position;
+	}
+}

File src/com/ra4king/fps/renderers/BulletRenderer.java

+package com.ra4king.fps.renderers;
+
+import static org.lwjgl.opengl.GL11.*;
+import static org.lwjgl.opengl.GL15.*;
+import static org.lwjgl.opengl.GL20.*;
+import static org.lwjgl.opengl.GL30.*;
+import static org.lwjgl.opengl.GL31.*;
+import static org.lwjgl.opengl.GL33.*;
+
+import java.nio.FloatBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+
+import org.lwjgl.BufferUtils;
+
+import com.ra4king.fps.GLUtils;
+import com.ra4king.fps.GLUtils.FrustumCulling;
+import com.ra4king.fps.actors.Bullet;
+import com.ra4king.fps.world.BulletManager;
+import com.ra4king.opengl.util.ShaderProgram;
+import com.ra4king.opengl.util.Utils;
+import com.ra4king.opengl.util.math.Matrix4;
+import com.ra4king.opengl.util.math.MatrixStack;
+import com.ra4king.opengl.util.math.Vector3;
+
+/**
+ * @author ra4king
+ */
+public class BulletRenderer {
+	private BulletManager bulletManager;
+	
+	private ShaderProgram bulletProgram;
+	private int projectionMatrixUniform, modelViewMatrixUniform;
+	
+	private FloatBuffer bulletDataBuffer;
+	private int vao, bulletDataVBO;
+	
+	private final boolean IS_MAC = System.getProperty("os.name").toLowerCase().contains("mac");
+	private final boolean HAS_VAO = GLUtils.get().VERSION >= 30;
+	
+	public BulletRenderer(BulletManager bulletManager) {
+		this.bulletManager = bulletManager;
+		
+		bulletDataBuffer = BufferUtils.createFloatBuffer(500 * 2 * 4);
+		
+		bulletProgram = new ShaderProgram(Utils.readFully(getClass().getResourceAsStream(GLUtils.get().SHADERS_ROOT_PATH + "bullet.vert")), Utils.readFully(getClass().getResourceAsStream(GLUtils.get().SHADERS_ROOT_PATH + "bullet.frag")));
+		
+		projectionMatrixUniform = bulletProgram.getUniformLocation("projectionMatrix");
+		modelViewMatrixUniform = bulletProgram.getUniformLocation("modelViewMatrix");
+		
+		bulletDataVBO = glGenBuffers();
+		glBindBuffer(GL_ARRAY_BUFFER, bulletDataVBO);
+		glBufferData(GL_ARRAY_BUFFER, bulletDataBuffer.capacity() * 4, GL_STREAM_DRAW);
+		
+		float[] bulletMappings = { -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, -0.5f,
+				0.5f, -0.5f, -0.5f, -0.5f, -0.5f, 0.5f };
+		
+		FloatBuffer bulletMappingsBuffer = BufferUtils.createFloatBuffer(bulletMappings.length);
+		bulletMappingsBuffer.put(bulletMappings).flip();
+		
+		int bulletMappingsVBO = glGenBuffers();
+		glBindBuffer(GL_ARRAY_BUFFER, bulletMappingsVBO);
+		glBufferData(GL_ARRAY_BUFFER, bulletMappingsBuffer, GL_STATIC_DRAW);
+		
+		vao = glGenVertexArrays();
+		GLUtils.get().glBindVertexArray(vao);
+		
+		glEnableVertexAttribArray(0);
+		glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, 0);
+		
+		glBindBuffer(GL_ARRAY_BUFFER, bulletDataVBO);
+		glEnableVertexAttribArray(1);
+		glEnableVertexAttribArray(2);
+		glVertexAttribPointer(1, 4, GL_FLOAT, false, 8 * 4, 0);
+		glVertexAttribPointer(2, 4, GL_FLOAT, false, 8 * 4, 4 * 4);
+		glVertexAttribDivisor(1, 1);
+		glVertexAttribDivisor(2, 1);
+		
+		glBindBuffer(GL_ARRAY_BUFFER, 0);
+		
+		GLUtils.get().glBindVertexArray(0);
+	}
+	
+	private HashMap<Bullet,Vector3> cameraWorldPositions = new HashMap<>();
+	
+	public void getBulletLightData(Matrix4 viewMatrix, FloatBuffer bulletData, int maxBulletCount) {
+		int bulletCount = 0;
+		
+		final float bulletK = 0.0001f, nonSolidBulletK = 0.05f;
+		
+		cameraWorldPositions.clear();
+		sort(viewMatrix);
+		
+		ArrayList<Bullet> bullets = bulletManager.getBullets();
+		
+		bulletData.put(Math.min(bullets.size(), maxBulletCount));
+		
+		for(Bullet b : bullets) {
+			if(bulletCount < maxBulletCount) {
+				bulletCount++;
+				
+				bulletData.put(cameraWorldPositions.get(b).toBuffer());
+				bulletData.put(b.getRange());
+				bulletData.put(b.getColor().toBuffer());
+				bulletData.put((b.isSolid() ? bulletK : nonSolidBulletK) / b.getAlpha());
+			}
+		}
+	}
+	
+	private void sort(Matrix4 viewMatrix) {
+		sort(viewMatrix, bulletManager.getBullets(), cameraWorldPositions);
+	}
+	
+	private void sort(Matrix4 viewMatrix, List<Bullet> bullets, final HashMap<Bullet,Vector3> bulletPositions) {
+		if(bulletPositions.isEmpty())
+			for(Bullet b : bullets)
+				bulletPositions.put(b, viewMatrix.mult(b.getPosition()));
+		
+		Collections.sort(bullets, new Comparator<Bullet>() {
+			@Override
+			public int compare(Bullet o1, Bullet o2) {
+				return (int)Math.signum(bulletPositions.get(o1).z() - bulletPositions.get(o2).z());
+			}
+		});
+	}
+	
+	public void render(Matrix4 projectionMatrix, MatrixStack modelViewMatrix, FrustumCulling culling) {
+		sort(modelViewMatrix.getTop());
+		
+		render(projectionMatrix, modelViewMatrix, bulletManager.getBullets(), culling);
+	}
+	
+	public void render(Matrix4 projectionMatrix, MatrixStack modelViewMatrix, FrustumCulling culling, Bullet ... bullets) {
+		List<Bullet> bulletList = Arrays.asList(bullets);
+		sort(modelViewMatrix.getTop(), bulletList, new HashMap<>());
+		
+		render(projectionMatrix, modelViewMatrix, bulletList, culling);
+	}
+	
+	private void render(Matrix4 projectionMatrix, MatrixStack modelViewMatrix, List<Bullet> bullets, FrustumCulling culling) {
+		if(bullets.size() == 0)
+			return;
+		
+		bulletProgram.begin();
+		
+		glUniformMatrix4(projectionMatrixUniform, false, projectionMatrix.toBuffer());
+		glUniformMatrix4(modelViewMatrixUniform, false, modelViewMatrix.getTop().toBuffer());
+		
+		boolean bulletCountChanged = bullets.size() * 2 * 4 > bulletDataBuffer.capacity();
+		
+		if(bulletCountChanged)
+			bulletDataBuffer = BufferUtils.createFloatBuffer(bullets.size() * 2 * 4);
+		else
+			bulletDataBuffer.clear();
+		
+		int bulletDrawnCount = 0;
+		
+		for(Bullet b : bullets) {
+			if(culling != null && !culling.isCubeInsideFrustum(b.getPosition(), b.getSize()))
+				continue;
+			
+			bulletDrawnCount++;
+			
+			bulletDataBuffer.put(b.getPosition().toBuffer()).put(b.getSize());
+			bulletDataBuffer.put(b.getColor().toBuffer()).put(b.getAlpha());
+		}
+		
+		bulletDataBuffer.flip();
+		
+		glBindBuffer(GL_ARRAY_BUFFER, bulletDataVBO);
+		
+		if(bulletCountChanged)
+			glBufferData(GL_ARRAY_BUFFER, bulletDataBuffer, GL_STREAM_DRAW);
+		else
+			glBufferSubData(GL_ARRAY_BUFFER, 0, bulletDataBuffer);
+		
+		glBindBuffer(GL_ARRAY_BUFFER, 0);
+		
+		GLUtils.get().glBindVertexArray(vao);
+		
+		glDepthMask(false);
+		glDrawArraysInstanced(GL_TRIANGLES, 0, 6, bulletDrawnCount);
+		glDepthMask(true);
+		
+		GLUtils.get().glBindVertexArray(0);
+		
+		bulletProgram.end();
+	}
+}

File src/com/ra4king/fps/renderers/WorldRenderer.java

+package com.ra4king.fps.renderers;
+
+import static org.lwjgl.opengl.GL11.*;
+import static org.lwjgl.opengl.GL15.*;
+import static org.lwjgl.opengl.GL20.*;
+import static org.lwjgl.opengl.GL30.*;
+import static org.lwjgl.opengl.GL31.*;
+import static org.lwjgl.opengl.GL32.*;
+import static org.lwjgl.opengl.GL33.*;
+
+import java.nio.FloatBuffer;
+
+import org.lwjgl.BufferUtils;
+import org.lwjgl.input.Keyboard;
+
+import com.ra4king.fps.Camera;
+import com.ra4king.fps.GLUtils;
+import com.ra4king.fps.GLUtils.FrustumCulling;
+import com.ra4king.fps.actors.Bullet;
+import com.ra4king.fps.world.Chunk;
+import com.ra4king.fps.world.World;
+import com.ra4king.opengl.util.ShaderProgram;
+import com.ra4king.opengl.util.Utils;
+import com.ra4king.opengl.util.math.Matrix4;
+import com.ra4king.opengl.util.math.MatrixStack;
+import com.ra4king.opengl.util.math.Vector3;
+
+/**
+ * @author ra4king
+ */
+public class WorldRenderer {
+	private World world;
+	
+	private BulletRenderer bulletRenderer;
+	
+	private int vao;
+	
+	private ShaderProgram worldProgram;
+	private int projectionMatrixUniform;
+	private int modelViewMatrixUniform;
+	
+	private static final int MAX_NUM_LIGHTS = 500;
+	
+	private FloatBuffer lightsBuffer;
+	private int lightsUniformBufferVBO;
+	
+	private FrustumCulling culling;
+	
+	public WorldRenderer(World world) {
+		this.world = world;
+		
+		glClearColor(0, 0, 0, 0);// 0.4f, 0.6f, 0.9f, 0f);
+		
+		glEnable(GL_DEPTH_TEST);
+		glDepthRange(0, 1);
+		
+		glDepthMask(true);
+		
+		glEnable(GL_CULL_FACE);
+		glCullFace(GL_BACK);
+		glFrontFace(GL_CW);
+		
+		glEnable(GL_BLEND);
+		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+		
+		if(GLUtils.get().VERSION >= 32)
+			glEnable(GL_DEPTH_CLAMP);
+		
+		// private int mainLightPositionUniform, mainDiffuseColorUniform, mainAmbientColorUniform;
+		// private int numberOfLightsUniform;
+		// private int lightPositionsUniform, lightColorsUniform;
+		
+		// private FloatBuffer lightsColorBuffer;
+		
+		// if(GLUtils.get().VERSION >= 33)
+		// worldProgram = new ShaderProgram(Utils.readFully(getClass().getResourceAsStream(GLUtils.get().SHADERS_ROOT_PATH + "fps.vert")),
+		// Utils.readFully(getClass().getResourceAsStream(GLUtils.get().SHADERS_ROOT_PATH + "fps.frag")));
+		// else if(GLUtils.get().VERSION >= 30)
+		// worldProgram = new ShaderProgram(Utils.readFully(getClass().getResourceAsStream(GLUtils.get().SHADERS_ROOT_PATH + "fps3.0.vert")),
+		// Utils.readFully(getClass().getResourceAsStream(GLUtils.get().SHADERS_ROOT_PATH + "fps3.0.frag")));
+		// else {
+		// throw new RuntimeException("2.1 and below not supported.");
+		//
+		// // worldProgram = new ShaderProgram(Utils.readFully(getClass().getResourceAsStream(SHADERS_ROOT_PATH + "fps2.1.vert")), Utils.readFully(getClass().getResourceAsStream(SHADERS_ROOT_PATH +
+		// // "fps2.1.frag")));
+		// }
+		
+		loadShaders();
+		
+		culling = new FrustumCulling();
+		
+		bulletRenderer = new BulletRenderer(world.getBulletManager());
+	}
+	
+	private void loadShaders() {
+		float[] unitCube = {
+				-0.5f, 0.5f, 0.5f,
+				0, 0, 1,
+				0.5f, 0.5f, 0.5f,
+				0, 0, 1,
+				0.5f, -0.5f, 0.5f,
+				0, 0, 1,
+				0.5f, -0.5f, 0.5f,
+				0, 0, 1,
+				-0.5f, -0.5f, 0.5f,
+				0, 0, 1,
+				-0.5f, 0.5f, 0.5f,
+				0, 0, 1,
+				
+				0.5f, 0.5f, -0.5f,
+				0, 0, -1,
+				-0.5f, 0.5f, -0.5f,
+				0, 0, -1,
+				-0.5f, -0.5f, -0.5f,
+				0, 0, -1,
+				-0.5f, -0.5f, -0.5f,
+				0, 0, -1,
+				0.5f, -0.5f, -0.5f,
+				0, 0, -1,
+				0.5f, 0.5f, -0.5f,
+				0, 0, -1,
+				
+				-0.5f, 0.5f, -0.5f,
+				0, 1, 0,
+				0.5f, 0.5f, -0.5f,
+				0, 1, 0,
+				0.5f, 0.5f, 0.5f,
+				0, 1, 0,
+				0.5f, 0.5f, 0.5f,
+				0, 1, 0,
+				-0.5f, 0.5f, 0.5f,
+				0, 1, 0,
+				-0.5f, 0.5f, -0.5f,
+				0, 1, 0,
+				
+				-0.5f, -0.5f, 0.5f,
+				0, -1, 0,
+				0.5f, -0.5f, 0.5f,
+				0, -1, 0,
+				0.5f, -0.5f, -0.5f,
+				0, -1, 0,
+				0.5f, -0.5f, -0.5f,
+				0, -1, 0,
+				-0.5f, -0.5f, -0.5f,
+				0, -1, 0,
+				-0.5f, -0.5f, 0.5f,
+				0, -1, 0,
+				
+				0.5f, 0.5f, 0.5f,
+				1, 0, 0,
+				0.5f, 0.5f, -0.5f,
+				1, 0, 0,
+				0.5f, -0.5f, -0.5f,
+				1, 0, 0,
+				0.5f, -0.5f, -0.5f,
+				1, 0, 0,
+				0.5f, -0.5f, 0.5f,
+				1, 0, 0,
+				0.5f, 0.5f, 0.5f,
+				1, 0, 0,
+				
+				-0.5f, 0.5f, -0.5f,
+				-1, 0, 0,
+				-0.5f, 0.5f, 0.5f,
+				-1, 0, 0,
+				-0.5f, -0.5f, 0.5f,
+				-1, 0, 0,
+				-0.5f, -0.5f, 0.5f,
+				-1, 0, 0,
+				-0.5f, -0.5f, -0.5f,
+				-1, 0, 0,
+				-0.5f, 0.5f, -0.5f,
+				-1, 0, 0,
+		};
+		
+		FloatBuffer cubeBuffer = BufferUtils.createFloatBuffer(unitCube.length);
+		cubeBuffer.put(unitCube).flip();
+		
+		glBindBuffer(GL_ARRAY_BUFFER, glGenBuffers());
+		glBufferData(GL_ARRAY_BUFFER, cubeBuffer, GL_STATIC_DRAW);
+		
+		worldProgram = new ShaderProgram(
+				Utils.readFully(getClass().getResourceAsStream(GLUtils.get().SHADERS_ROOT_PATH + "fps.vert")),
+				Utils.readFully(getClass().getResourceAsStream(GLUtils.get().SHADERS_ROOT_PATH + "fps.frag")));
+		
+		vao = GLUtils.get().glGenVertexArrays();
+		GLUtils.get().glBindVertexArray(vao);
+		
+		glEnableVertexAttribArray(0);
+		glEnableVertexAttribArray(1);
+		glVertexAttribDivisor(0, 1);
+		glVertexAttribDivisor(1, 1);
+		
+		glEnableVertexAttribArray(2);
+		glEnableVertexAttribArray(3);
+		glVertexAttribPointer(2, 3, GL_FLOAT, false, 6 * 4, 0);
+		glVertexAttribPointer(3, 3, GL_FLOAT, false, 6 * 4, 3 * 4);
+		
+		glBindBuffer(GL_ARRAY_BUFFER, 0);
+		GLUtils.get().glBindVertexArray(0);
+		
+		lightsBuffer = BufferUtils.createFloatBuffer(MAX_NUM_LIGHTS * 2 * 4 + 2 * 4);
+		
+		int lightsBlockIndex = worldProgram.getUniformBlockIndex("Lights");
+		glUniformBlockBinding(worldProgram.getProgram(), lightsBlockIndex, 1);
+		
+		lightsUniformBufferVBO = glGenBuffers();
+		glBindBuffer(GL_UNIFORM_BUFFER, lightsUniformBufferVBO);
+		glBufferData(GL_UNIFORM_BUFFER, lightsBuffer.capacity() * 4, GL_STREAM_DRAW);
+		glBindBufferBase(GL_UNIFORM_BUFFER, 1, lightsUniformBufferVBO);
+		glBindBuffer(GL_UNIFORM_BUFFER, 0);
+		
+		projectionMatrixUniform = worldProgram.getUniformLocation("projectionMatrix");
+		modelViewMatrixUniform = worldProgram.getUniformLocation("modelViewMatrix");
+	}
+	
+	private long timePassed;
+	private int frameCount, chunksRendered;
+	
+	private boolean isKeyDown;
+	
+	public void update(long deltaTime) {
+		timePassed += deltaTime;
+		
+		if(Keyboard.isKeyDown(Keyboard.KEY_R) && !isKeyDown) {
+			loadShaders();
+			System.out.println("Shaders reloaded.");
+			isKeyDown = true;
+		}
+		else
+			isKeyDown = false;
+		
+		while(timePassed >= 1e9) {
+			timePassed -= 1e9;
+			
+			if(frameCount == 0)
+				continue;
+			
+			System.out.println("Average of " + (chunksRendered / frameCount) + " chunks rendered.");
+			
+			long cubesRendered = 0;
+			for(Chunk c : world.getChunkManager().getChunks())
+				cubesRendered += c.getCubesRendered();
+			
+			System.out.println("Average of " + (cubesRendered / frameCount) + " cubes renderered.\n");
+			
+			frameCount = chunksRendered = 0;
+		}
+	}
+	
+	private final Vector3 mainDiffuseColor = new Vector3(1f, 1f, 1f);
+	private final Vector3 mainAmbientColor = new Vector3(0.01f, 0.01f, 0.01f);
+	
+	private final Bullet aim = new Bullet(new Vector3(), new Vector3(), 4, 0, Long.MAX_VALUE, false, new Vector3(1));
+	
+	private final Matrix4 viewMatrix = new Matrix4(), cullingProjectionMatrix = new Matrix4();
+	private final MatrixStack tempStack = new MatrixStack();
+	
+	private final Vector3 renderTemp = new Vector3();
+	
+	public void render() {
+		worldProgram.begin();
+		
+		Camera camera = world.getCamera();
+		
+		world.getCamera().getOrientation().toMatrix(viewMatrix).translate(camera.getPosition());
+		
+		glUniformMatrix4(projectionMatrixUniform, false, camera.getProjectionMatrix().toBuffer());
+		glUniformMatrix4(modelViewMatrixUniform, false, viewMatrix.toBuffer());
+		
+		culling.setupPlanes(cullingProjectionMatrix.set(camera.getProjectionMatrix()).mult(viewMatrix));
+		
+		final float mainK = 0.001f;
+		
+		lightsBuffer.clear();
+		
+		lightsBuffer.put(mainDiffuseColor.toBuffer());
+		lightsBuffer.put(mainK);
+		
+		lightsBuffer.put(mainAmbientColor.toBuffer());
+		
+		bulletRenderer.getBulletLightData(viewMatrix, lightsBuffer, MAX_NUM_LIGHTS);
+		
+		lightsBuffer.flip();
+		
+		glBindBuffer(GL_UNIFORM_BUFFER, lightsUniformBufferVBO);
+		glBufferSubData(GL_UNIFORM_BUFFER, 0, lightsBuffer);
+		glBindBuffer(GL_UNIFORM_BUFFER, 0);
+		
+		GLUtils.get().glBindVertexArray(vao);
+		
+		for(Chunk chunk : world.getChunkManager().getChunks())
+			if(culling.isCubeInsideFrustum(renderTemp.set(chunk.getChunkInfo().chunkCornerX, chunk.getChunkInfo().chunkCornerY, -chunk.getChunkInfo().chunkCornerZ).mult(Chunk.SPACING), Chunk.CUBES_SIDE * Chunk.SPACING * 2)) {
+				chunk.render();
+				chunksRendered++;
+			}
+		
+		frameCount++;
+		
+		GLUtils.get().glBindVertexArray(0);
+		
+		worldProgram.end();
+		
+		bulletRenderer.render(camera.getProjectionMatrix(), tempStack.clear().setTop(viewMatrix), culling);
+		
+		glDepthMask(false);
+		
+		glDisable(GL_DEPTH_TEST);
+		bulletRenderer.render(new Matrix4().clearToOrtho(-GLUtils.get().getWidth() / 2, GLUtils.get().getWidth() / 2, -GLUtils.get().getHeight() / 2, GLUtils.get().getHeight() / 2, -1, 1), new MatrixStack(), null, aim);
+		glEnable(GL_DEPTH_TEST);
+		
+		glDepthMask(true);
+	}
+}

File src/com/ra4king/fps/world/BulletManager.java

+package com.ra4king.fps.world;
+
+import java.util.ArrayList;
+
+import com.ra4king.fps.actors.Bullet;
+import com.ra4king.fps.world.Chunk.BlockInfo;
+import com.ra4king.opengl.util.math.Vector3;
+
+/**
+ * @author ra4king
+ */
+public class BulletManager {
+	private ArrayList<Bullet> bullets;
+	
+	private ChunkManager chunkManager;
+	
+	public BulletManager(ChunkManager chunkManager) {
+		this.chunkManager = chunkManager;
+		
+		bullets = new ArrayList<>();
+	}
+	
+	public void addBullet(Bullet bullet) {
+		bullets.add(bullet);
+	}
+	
+	public ArrayList<Bullet> getBullets() {
+		return bullets;
+	}
+	
+	private int blocksDestroyed;
+	
+	public int getBlocksDestroyedCount() {
+		return blocksDestroyed;
+	}
+
+	public void update(long deltaTime) {
+		ArrayList<Bullet> temp = new ArrayList<>();
+		
+		for(Bullet bullet : bullets) {
+			bullet.update(deltaTime);
+			
+			if(bullet.isAlive()) {
+				boolean isAlive = true;
+				
+				if(false) {// bullet.isSolid()) {
+					Vector3 pos = bullet.getPosition();
+					
+					BlockInfo block;
+					if((block = chunkManager.getBlock(pos, 0.6f * bullet.getSize())) != null) {
+						if(!chunkManager.removeBlock(block))
+							System.out.println("UH OH!");
+						else
+							blocksDestroyed++;
+						
+						isAlive = false;
+						
+						for(int a = -1; a < 2; a++) {
+							for(int b = -1; b < 2; b++) {
+								for(int c = -1; c < 2; c++) {
+									if(a != 0 && ((a == b && b == c) || (a == -b && b == -c)))
+										temp.add(new Bullet(pos, new Vector3(a, b, c).normalize().mult(100), 1, 500, (long)2.5e8, false, new Vector3(1, 1, 1)));
+								}
+							}
+						}
+					}
+				}
+				
+				if(isAlive)
+					temp.add(bullet);
+			}
+		}
+		
+		bullets.clear();
+		bullets = temp;
+	}
+}

File src/com/ra4king/fps/world/Chunk.java

+package com.ra4king.fps.world;
+
+import static org.lwjgl.opengl.GL11.*;
+import static org.lwjgl.opengl.GL15.*;
+import static org.lwjgl.opengl.GL20.*;
+import static org.lwjgl.opengl.GL31.*;
+
+import java.nio.FloatBuffer;
+
+import org.lwjgl.BufferUtils;
+
+/**
+ * @author ra4king
+ */
+public class Chunk {
+	public enum BlockType {
+		DIRT(1);
+		
+		public final int order;
+		
+		private BlockType(int order) {
+			this.order = order;
+		}
+	}
+	
+	public static class BlockInfo {
+		public final ChunkInfo chunkInfo;
+		public final int x, y, z;
+		public BlockType type;