Merge pull request #1 from nopoe/master

Added new ray casting method using Daniel Cohen's voxel traversal algorithm
master
matortheeternal 2014-05-24 22:59:01 -07:00
commit 4b02ef303a
3 changed files with 156 additions and 2 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.class
*.jar

1
VoxelEngine/bin/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/voxelengine

View File

@ -1,9 +1,11 @@
package voxelengine;
import java.awt.Color;
import java.awt.image.MemoryImageSource;
import static java.lang.Math.*;
import java.awt.*;
import java.awt.image.*;
import java.util.concurrent.*;
public class Renderer {
private World world;
private Color SkyboxColor = Color.BLACK;
@ -309,6 +311,123 @@ public class Renderer {
return SkyboxColor.getRGB();
}
public MemoryImageSource renderG(int width, int height, int pixelScale, int castScale) {
int[] mem = new int[width * height];
double yaw = camera.rotY;
double pitch = camera.rotX;
double[][] ref = new double[][] { { sin(pitch) * cos(yaw), sin(pitch) * sin(yaw), -cos(pitch) },
{ -sin(yaw), cos(yaw), 0 }, // equal to cos(yaw + PI/2), sin(yaw + PI/2), 0
{ cos(pitch) * cos(yaw), cos(pitch) * sin(yaw), 2 * sin(pitch) } // cross product of the two vectors
// above
};
// raycast for each pixel
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 {
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));
}
}
}
try {
service.shutdown();
service.awaitTermination(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// return image source
return new MemoryImageSource(width, height, mem, 0, width);
}
private int raycastG(int px, int py, int width, int height, double[][] ref) {
double w2 = width / 2.0;
double h2 = height / 2.0;
double x = camera.x;
double y = camera.y;
double z = camera.z;
double fovH = camera.fovH / 2;
double fovV = camera.fovV / 2;
double yawr = ((px - w2) / w2) * fovH;
double pitchr = ((py - h2) / h2) * fovV; // correction because view window isn't 1:1
double[] ray = new double[] { cos(pitchr) * cos(yawr), cos(pitchr) * sin(yawr), -sin(pitchr) };
ray = new double[] { ray[0] * ref[0][0] + ray[1] * ref[1][0] + ray[2] * ref[2][0],
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], };
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;
int sx, sy, sz, n;
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);
bx = 2 * ax;
by = 2 * ay;
bz = 2 * az;
exy = ay - ax;
exz = az - ax;
ezy = ay - az;
n = (int) (ax + ay + az);
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) {
x += sx;
exy += by;
exz += bz;
} else {
z += sz;
exz -= bx;
ezy += by;
}
} else {
if (ezy < 0) {
z += sz;
exz -= bx;
ezy += by;
} else {
y += sy;
exy -= bx;
ezy -= bz;
}
}
}
return SkyboxColor.getRGB();
}
private Color CalculateColor(Color c, double x1, double x2, double y1, double y2, double z1, double z2) {
double distance = Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2) + Math.pow(z1 - z2, 2));
double lightIntensity = falloff / Math.pow(distance, rate);
@ -324,4 +443,36 @@ public class Renderer {
// return calculated color
return new Color(red, green, blue);
}
private class PixelWorker implements Runnable {
private int[] framebuffer;
private int[] fbIndices;
private int px;
private int py;
private int width;
private int height;
private double[][] ref;
public PixelWorker(int[] framebuffer, int[] fbIndices, int px, int py, int width, int height, double[][] ref) {
this.framebuffer = framebuffer;
this.fbIndices = fbIndices;
this.px = px;
this.py = py;
this.width = width;
this.height = height;
this.ref = ref;
}
@Override
public void run() {
int result = Renderer.this.raycastG(px, py, width, height, ref);
synchronized (framebuffer) {
for (int index : fbIndices) {
framebuffer[index] = result;
}
}
}
}
}