diff --git a/.gitignore b/.gitignore index 8e9e794..f7049a2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *.class *.jar +*.policy.applet \ No newline at end of file diff --git a/VoxelEngine/bin/java.policy.applet b/VoxelEngine/bin/java.policy.applet deleted file mode 100644 index ba9f51d..0000000 --- a/VoxelEngine/bin/java.policy.applet +++ /dev/null @@ -1,7 +0,0 @@ -/* AUTOMATICALLY GENERATED ON Tue Apr 16 17:20:59 EDT 2002*/ -/* DO NOT EDIT */ - -grant { - permission java.security.AllPermission; -}; - diff --git a/VoxelEngine/bin/voxelengine/Block.class b/VoxelEngine/bin/voxelengine/Block.class deleted file mode 100644 index c9af665..0000000 Binary files a/VoxelEngine/bin/voxelengine/Block.class and /dev/null differ diff --git a/VoxelEngine/bin/voxelengine/BlockLibrary.class b/VoxelEngine/bin/voxelengine/BlockLibrary.class deleted file mode 100644 index 227ed66..0000000 Binary files a/VoxelEngine/bin/voxelengine/BlockLibrary.class and /dev/null differ diff --git a/VoxelEngine/bin/voxelengine/BlockType.class b/VoxelEngine/bin/voxelengine/BlockType.class deleted file mode 100644 index 0f26d6d..0000000 Binary files a/VoxelEngine/bin/voxelengine/BlockType.class and /dev/null differ diff --git a/VoxelEngine/bin/voxelengine/Camera.class b/VoxelEngine/bin/voxelengine/Camera.class deleted file mode 100644 index 5306510..0000000 Binary files a/VoxelEngine/bin/voxelengine/Camera.class and /dev/null differ diff --git a/VoxelEngine/bin/voxelengine/Chunk.class b/VoxelEngine/bin/voxelengine/Chunk.class deleted file mode 100644 index 3b20f8c..0000000 Binary files a/VoxelEngine/bin/voxelengine/Chunk.class and /dev/null differ diff --git a/VoxelEngine/bin/voxelengine/Keyboard.class b/VoxelEngine/bin/voxelengine/Keyboard.class deleted file mode 100644 index 951665e..0000000 Binary files a/VoxelEngine/bin/voxelengine/Keyboard.class and /dev/null differ diff --git a/VoxelEngine/bin/voxelengine/Menu.class b/VoxelEngine/bin/voxelengine/Menu.class deleted file mode 100644 index 679f2f7..0000000 Binary files a/VoxelEngine/bin/voxelengine/Menu.class and /dev/null differ diff --git a/VoxelEngine/bin/voxelengine/Palette.class b/VoxelEngine/bin/voxelengine/Palette.class deleted file mode 100644 index a99233a..0000000 Binary files a/VoxelEngine/bin/voxelengine/Palette.class and /dev/null differ diff --git a/VoxelEngine/bin/voxelengine/Renderer.class b/VoxelEngine/bin/voxelengine/Renderer.class deleted file mode 100644 index 3c53f7e..0000000 Binary files a/VoxelEngine/bin/voxelengine/Renderer.class and /dev/null differ diff --git a/VoxelEngine/bin/voxelengine/SwingInterface$Worker.class b/VoxelEngine/bin/voxelengine/SwingInterface$Worker.class deleted file mode 100644 index faef5ab..0000000 Binary files a/VoxelEngine/bin/voxelengine/SwingInterface$Worker.class and /dev/null differ diff --git a/VoxelEngine/bin/voxelengine/SwingInterface.class b/VoxelEngine/bin/voxelengine/SwingInterface.class deleted file mode 100644 index 841f853..0000000 Binary files a/VoxelEngine/bin/voxelengine/SwingInterface.class and /dev/null differ diff --git a/VoxelEngine/bin/voxelengine/World.class b/VoxelEngine/bin/voxelengine/World.class deleted file mode 100644 index d61eaf3..0000000 Binary files a/VoxelEngine/bin/voxelengine/World.class and /dev/null differ diff --git a/VoxelEngine/src/voxelengine/RayCastCollision.java b/VoxelEngine/src/voxelengine/RayCastCollision.java new file mode 100644 index 0000000..2cfb111 --- /dev/null +++ b/VoxelEngine/src/voxelengine/RayCastCollision.java @@ -0,0 +1,57 @@ +package voxelengine; + +public class RayCastCollision { + private Block collisionBlock; + private int face; + + private double x, y, z; + + public RayCastCollision(Block collisionBlock, int face, double x, double y, double z) { + super(); + this.collisionBlock = collisionBlock; + this.face = face; + this.x = x; + this.y = y; + this.z = z; + } + + public Block getCollisionBlock() { + return collisionBlock; + } + + public void setCollisionBlock(Block collisionBlock) { + this.collisionBlock = collisionBlock; + } + + public int getFace() { + return face; + } + + public void setFace(int face) { + this.face = face; + } + + public double getX() { + return x; + } + + public void setX(double x) { + this.x = x; + } + + public double getY() { + return y; + } + + public void setY(double y) { + this.y = y; + } + + public double getZ() { + return z; + } + + public void setZ(double z) { + this.z = z; + } +} diff --git a/VoxelEngine/src/voxelengine/Renderer.java b/VoxelEngine/src/voxelengine/Renderer.java index 29c0335..4376bc0 100644 --- a/VoxelEngine/src/voxelengine/Renderer.java +++ b/VoxelEngine/src/voxelengine/Renderer.java @@ -9,8 +9,10 @@ import java.util.concurrent.*; public class Renderer { private World world; private Color SkyboxColor = Color.BLACK; - private double falloff = 8.0; - private double rate = 0.85; + private double falloff = 4.0; + private double rate = 0.6; + private double ambientIntensity = 0.2; + private double directionalIntensity = 0.3; private int renderDistance = 8; public Camera camera; @@ -311,8 +313,13 @@ public class Renderer { return SkyboxColor.getRGB(); } - public MemoryImageSource renderG(int width, int height, int pixelScale, int castScale) { - int[] mem = new int[width * height]; + int[] mem = new int[0]; + MemoryImageSource fb; + + public MemoryImageSource renderG(int width, int height, int pixelScale, int castScale, boolean doShadows) { + if (mem.length != width * height) { + mem = new int[width * height]; // this is very marginally faster than recreating it every frame + } double yaw = camera.rotY; double pitch = camera.rotX; double[][] ref = new double[][] { { sin(pitch) * cos(yaw), sin(pitch) * sin(yaw), -cos(pitch) }, @@ -325,32 +332,38 @@ public class Renderer { ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); for (int py = 0; py < height - pixelScale; py += pixelScale) { for (int px = 0; px < width - pixelScale; px += pixelScale) { - if (pixelScale == 1) - service.execute(new PixelWorker(mem, new int[] { px + py * width }, px, py, width, height, ref)); - else { + if (pixelScale == 1) { + service.execute(new PixelWorker(mem, new int[] { px + py * width }, px, py, width, height, ref, + doShadows)); + } else { int[] fbIndices = new int[pixelScale * pixelScale]; for (int pys = 0; pys < pixelScale; ++pys) { for (int pxs = 0; pxs < pixelScale; ++pxs) { fbIndices[pys * pixelScale + pxs] = px + pxs + (py + pys) * width; } } - service.execute(new PixelWorker(mem, fbIndices, px, py, width, height, ref)); + service.execute(new PixelWorker(mem, fbIndices, px, py, width, height, ref, doShadows)); } } } try { service.shutdown(); - service.awaitTermination(1, TimeUnit.SECONDS); + service.awaitTermination(10, TimeUnit.SECONDS); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } + if (fb == null) { + fb = new MemoryImageSource(width, height, mem, 0, width); + } else { + fb.newPixels(); // this is very marginally faster than recreating it + } // return image source - return new MemoryImageSource(width, height, mem, 0, width); + return fb; } - private int raycastG(int px, int py, int width, int height, double[][] ref) { + private RayCastCollision raycastGScreenCoords(int px, int py, int width, int height, double[][] ref) { double w2 = width / 2.0; double h2 = height / 2.0; @@ -368,64 +381,93 @@ public class Renderer { ray[0] * ref[0][1] + ray[1] * ref[1][1] + ray[2] * ref[2][1], ray[0] * ref[0][2] + ray[1] * ref[1][2] + ray[2] * ref[2][2], }; + return raycastG(x, y, z, ray); + } + + private RayCastCollision raycastG(double x, double y, double z, double[] ray) { + + double startX = x; + double startY = y; + double startZ = z; + double dx = ray[0] * renderDistance * 16; double dy = ray[1] * renderDistance * 16; double dz = ray[2] * renderDistance * 16; - double exy, exz, ezy, ax, ay, az, bx, by, bz; + double rayMagnitude = Math.sqrt(dx * dx + dy * dy + dz * dz); + + double ax, ay, az; int sx, sy, sz, n; + double sv = Double.MIN_NORMAL; + sx = (int) Math.signum(dx); sy = (int) Math.signum(dy); sz = (int) Math.signum(dz); - ax = Math.abs(dx); - ay = Math.abs(dy); - az = Math.abs(dz); + ax = Math.abs(dx) / rayMagnitude; + ay = Math.abs(dy) / rayMagnitude; + az = Math.abs(dz) / rayMagnitude; - bx = 2 * ax; - by = 2 * ay; - bz = 2 * az; + ax = ((ax > sv) ? ax : sv); + ay = ((ay > sv) ? ay : sv); + az = ((az > sv) ? az : sv); - exy = ay - ax; - exz = az - ax; - ezy = ay - az; + double tDeltaX = 1 / ax; + double tDeltaY = 1 / ay; + double tDeltaZ = 1 / az; - n = (int) (ax + ay + az); + double tMaxX = Math.abs((sx == 1) ? (1 - (x % 1.0)) : (x % 1.0)) / ax; + double tMaxY = Math.abs((sy == 1) ? (1 - (y % 1.0)) : (y % 1.0)) / ay; + double tMaxZ = Math.abs((sz == 1) ? (1 - (z % 1.0)) : (z % 1.0)) / az; + + n = (int) (Math.abs(dx) + Math.abs(dy) + Math.abs(dz)); + + int face = -1; while (n-- != 0) { - Block block = world.getBlock(x, y, z); - if (block != null) { - Color c = block.getType().getColor(); - c = CalculateColor(c, camera.x, x, camera.y, y, camera.z, z); - return c.getRGB(); - } - - if (exy < 0) { - if (exz < 0) { + if (tMaxX < tMaxY) { + if (tMaxX < tMaxZ) { + face = 0; x += sx; - exy += by; - exz += bz; + tMaxX += tDeltaX; } else { + face = 2; z += sz; - exz -= bx; - ezy += by; + tMaxZ += tDeltaZ; } } else { - if (ezy < 0) { - z += sz; - exz -= bx; - ezy += by; - } else { + if (tMaxY < tMaxZ) { + face = 1; y += sy; - exy -= bx; - ezy -= bz; + tMaxY += tDeltaY; + } else { + face = 2; + z += sz; + tMaxZ += tDeltaZ; } } + Block block = world.getBlock(x, y, z); + if (block != null) { + double t = 0; + switch (face) { + case 0: + t = tMaxX - tDeltaX - 0.01; + break; + case 1: + t = tMaxY - tDeltaY - 0.01; + break; + case 2: + t = tMaxZ - tDeltaZ - 0.01; + break; + } + return new RayCastCollision(block, face, t * dx / rayMagnitude + startX, + t * dy / rayMagnitude + startY, t * dz / rayMagnitude + startZ); + } } - return SkyboxColor.getRGB(); + return null; } private Color CalculateColor(Color c, double x1, double x2, double y1, double y2, double z1, double z2) { @@ -444,6 +486,42 @@ public class Renderer { return new Color(red, green, blue); } + private Color CalculateColor(Color c, double x1, double x2, double y1, double y2, double z1, double z2, int face, + double directionalDot) { + double ray[] = { x1 - x2, y1 - y2, z1 - z2 }; + double distance = Math.sqrt(ray[0] * ray[0] + ray[1] * ray[1] + ray[2] * ray[2]); + ray[0] /= distance; + ray[1] /= distance; + ray[2] /= distance; + double dot = 0; + switch (face) { + case 0: + dot = Math.abs(ray[0]); + break; + case 1: + dot = Math.abs(ray[1]); + break; + case 2: + dot = Math.abs(ray[2]); + break; + } + double diffuseIntensity = Math.max(falloff / Math.pow(distance, rate), 1.0) * dot; + double lightIntensity = ambientIntensity + (1 - ambientIntensity - directionalIntensity) * diffuseIntensity; + + lightIntensity += directionalIntensity * directionalDot; + + // calculate color components + int red = (int) (c.getRed() * lightIntensity); + red = (red > 255) ? 255 : red; + int green = (int) (c.getGreen() * lightIntensity); + green = (green > 255) ? 255 : green; + int blue = (int) (c.getBlue() * lightIntensity); + blue = (blue > 255) ? 255 : blue; + + // return calculated color + return new Color(red, green, blue); + } + private class PixelWorker implements Runnable { private int[] framebuffer; @@ -453,8 +531,10 @@ public class Renderer { private int width; private int height; private double[][] ref; + private boolean doShadows; - public PixelWorker(int[] framebuffer, int[] fbIndices, int px, int py, int width, int height, double[][] ref) { + public PixelWorker(int[] framebuffer, int[] fbIndices, int px, int py, int width, int height, double[][] ref, + boolean doShadows) { this.framebuffer = framebuffer; this.fbIndices = fbIndices; this.px = px; @@ -462,14 +542,48 @@ public class Renderer { this.width = width; this.height = height; this.ref = ref; + this.doShadows = doShadows; } + double[] lightRay = { renderDistance * 8, renderDistance * 2, renderDistance * 4 }; + @Override public void run() { - int result = Renderer.this.raycastG(px, py, width, height, ref); + RayCastCollision result = Renderer.this.raycastGScreenCoords(px, py, width, height, ref); + Color c; + double lightRayMagnitude = Math.sqrt(lightRay[0] * lightRay[0] + lightRay[1] * lightRay[1] + lightRay[2] + * lightRay[2]); + if (result != null) { + Block block = result.getCollisionBlock(); + double x = result.getX(); + double y = result.getY(); + double z = result.getZ(); + double lightDot = 0; + if (doShadows) { + RayCastCollision lightOcclusion = Renderer.this.raycastG(result.getX(), result.getY(), + result.getZ(), lightRay); + if (lightOcclusion == null) { + switch (result.getFace()) { + case 0: + lightDot = Math.abs(lightRay[0] / lightRayMagnitude); + break; + case 1: + lightDot = Math.abs(lightRay[1] / lightRayMagnitude); + break; + case 2: + lightDot = Math.abs(lightRay[2] / lightRayMagnitude); + break; + } + } + } + c = Renderer.this.CalculateColor(block.getType().getColor(), camera.x, x, camera.y, y, camera.z, z, + result.getFace(), lightDot); + } else { + c = SkyboxColor; + } synchronized (framebuffer) { for (int index : fbIndices) { - framebuffer[index] = result; + framebuffer[index] = c.getRGB(); } } } diff --git a/VoxelEngine/src/voxelengine/SwingInterface.java b/VoxelEngine/src/voxelengine/SwingInterface.java index fc23fd2..0f76bf5 100644 --- a/VoxelEngine/src/voxelengine/SwingInterface.java +++ b/VoxelEngine/src/voxelengine/SwingInterface.java @@ -24,7 +24,9 @@ public class SwingInterface extends JPanel { private static final int Y_SIZE = 600; private static int pixelScale = 4; private static int castScale = 4; - + + private static boolean doShadows = false; + public SwingInterface() { new Worker().execute(); } @@ -48,7 +50,9 @@ public class SwingInterface extends JPanel { set_up(); int frames = 0; long time = System.currentTimeMillis(); - + + long lastFrameNanos = System.nanoTime(); + while (true) { frames++; if (frames == 30) { @@ -58,11 +62,12 @@ public class SwingInterface extends JPanel { frames = 0; time = System.currentTimeMillis(); } - process_input(); - Image img = createImage(renderer.renderF(X_SIZE, Y_SIZE, pixelScale, castScale)); + process_input((System.nanoTime() - lastFrameNanos) / 1000000000.0); + lastFrameNanos = System.nanoTime(); + Image img = createImage(renderer.renderG(X_SIZE, Y_SIZE, pixelScale, castScale, doShadows)); BufferedImage buffer = new BufferedImage(X_SIZE, Y_SIZE, BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = buffer.createGraphics(); - g2.drawImage(img, 0, 0, null); + g2.drawImage(img, 0, 0, null); g2.dispose(); publish(buffer); } @@ -103,41 +108,44 @@ public class SwingInterface extends JPanel { renderer = new Renderer(world, camera); System.out.println("World set up. Now rendering..."); } - - private void process_input() { + + private void process_input(double timestep) { + final int movement_scale = 15; + final int rotation_scale = 15; + if (keyboard.isKeyDown('A')) { - camera.x += Math.cos(camera.rotY - Math.PI / 2); - camera.y += Math.sin(camera.rotY - Math.PI / 2); + camera.x += timestep * movement_scale * Math.cos(camera.rotY - Math.PI / 2); + camera.y += timestep * movement_scale * Math.sin(camera.rotY - Math.PI / 2); } if (keyboard.isKeyDown('D')) { - camera.x += Math.cos(camera.rotY + Math.PI / 2); - camera.y += Math.sin(camera.rotY + Math.PI / 2); + camera.x += timestep * movement_scale * Math.cos(camera.rotY + Math.PI / 2); + camera.y += timestep * movement_scale * Math.sin(camera.rotY + Math.PI / 2); } if (keyboard.isKeyDown('W')) { - camera.x += Math.cos(camera.rotY); - camera.y += Math.sin(camera.rotY); + camera.x += timestep * movement_scale * Math.cos(camera.rotY); + camera.y += timestep * movement_scale * Math.sin(camera.rotY); } if (keyboard.isKeyDown('S')) { - camera.x += Math.cos(camera.rotY + Math.PI); - camera.y += Math.sin(camera.rotY + Math.PI); + camera.x += timestep * movement_scale * Math.cos(camera.rotY + Math.PI); + camera.y += timestep * movement_scale * Math.sin(camera.rotY + Math.PI); } if (keyboard.isKeyDown('Q')) { - camera.z -= 1; + camera.z -= timestep * movement_scale; } if (keyboard.isKeyDown('E')) { - camera.z += 1; + camera.z += timestep * movement_scale; } if (keyboard.isKeyDown('J')) { - camera.rotY -= Math.PI / 32; + camera.rotY -= timestep * rotation_scale * Math.PI / 32; } if (keyboard.isKeyDown('L')) { - camera.rotY += Math.PI / 32; + camera.rotY += timestep * rotation_scale * Math.PI / 32; } if (keyboard.isKeyDown('I')) { - camera.rotX += Math.PI / 32; + camera.rotX += timestep * rotation_scale * Math.PI / 32; } if (keyboard.isKeyDown('K')) { - camera.rotX -= Math.PI / 32; + camera.rotX -= timestep * rotation_scale * Math.PI / 32; } if (keyboard.isKeyDown('T')) { pixelScale++; @@ -151,6 +159,13 @@ public class SwingInterface extends JPanel { if (keyboard.isKeyDown('H')) { castScale--; } + if (keyboard.isKeyDown('P')) { // P is for pretty + doShadows = true; + pixelScale = 1; + } else { + doShadows = false; + pixelScale = 4; + } camera.rotY = camera.rotY % (Math.PI * 2); camera.rotX = Math.max(Math.min(camera.rotX, Math.PI), 0); pixelScale = Math.max(Math.min(pixelScale, 10), 1);