Permalink
Browse files

Add detailed commentary to World raycast.

  • Loading branch information...
1 parent 78708eb commit 89d5e6de4d683ad5c865d76a6ec8edc24c5b9adf @kpreid committed Feb 18, 2013
Showing with 35 additions and 5 deletions.
  1. +35 −5 world.js
View
@@ -308,10 +308,10 @@
* Call the callback with (x,y,z,value,face) of all blocks along the line
* segment from point 'origin' in vector direction 'direction' of length
* 'radius'. 'radius' may be infinite.
- *
+ *
* 'face' is the normal vector of the face of that block that was entered.
* It should not be used after the callback returns.
- *
+ *
* If the callback returns a true value, the traversal will be stopped.
*/
function raycast(origin, direction, radius, callback) {
@@ -323,43 +323,71 @@
// • Imposed a distance limit.
// • The face passed through to reach the current cube is provided to
// the callback.
+
+ // The foundation of this algorithm is a parameterized representation of
+ // the provided ray,
+ // origin + t * direction,
+ // except that t is not actually stored; rather, at any given point in the
+ // traversal, we keep track of the *greater* t values which we would have
+ // if we took a step sufficient to cross a cube boundary along that axis
+ // (i.e. change the integer part of the coordinate) in the variables
+ // tMaxX, tMaxY, and tMaxZ.
+
+ // Cube containing origin point.
var x = Math.floor(origin[0]);
var y = Math.floor(origin[1]);
var z = Math.floor(origin[2]);
+ // Break out direction vector.
var dx = direction[0];
var dy = direction[1];
var dz = direction[2];
+ // Direction to increment x,y,z when stepping.
var stepX = signum(dx);
var stepY = signum(dy);
var stepZ = signum(dz);
+ // See description above. The initial values depend on the fractional
+ // part of the origin.
var tMaxX = intbound(origin[0], dx);
var tMaxY = intbound(origin[1], dy);
var tMaxZ = intbound(origin[2], dz);
+ // The change in t when taking a step (always positive).
var tDeltaX = stepX/dx;
var tDeltaY = stepY/dy;
var tDeltaZ = stepZ/dz;
+ // Buffer for reporting faces to the callback.
var face = vec3.create();
- if (dx === 0 && dy === 0 && dz === 0) throw new Error("Raycast in zero direction!");
+ // Avoids an infinite loop.
+ if (dx === 0 && dy === 0 && dz === 0)
+ throw new RangeError("Raycast in zero direction!");
- // 't' is in units of 'direction', so adjust radius in blocks by that
+ // Rescale from units of 1 cube-edge to units of 'direction' so we can
+ // compare with 't'.
radius /= Math.sqrt(dx*dx+dy*dy+dz*dz);
- //console.log(stepX, stepY, stepZ, dx, dy, dz, tDeltaX, tDeltaY, tDeltaZ);
while (/* ray has not gone past bounds of world */
(stepX > 0 ? x < wx : x >= 0) &&
(stepY > 0 ? y < wy : y >= 0) &&
(stepZ > 0 ? z < wz : z >= 0)) {
+ // Invoke the callback, unless we are not *yet* within the bounds of the
+ // world.
if (!(x < 0 || y < 0 || z < 0 || x >= wx || y >= wy || z >= wz))
if (callback(x, y, z, blocks[x*wy*wz + y*wz + z], face))
break;
+ // tMaxX stores the t-value at which we cross a cube boundary along the
+ // X axis, and similarly for Y and Z. Therefore, choosing the least tMax
+ // chooses the closest cube boundary. Only the first case of the four
+ // has been commented in detail.
if (tMaxX < tMaxY) {
if (tMaxX < tMaxZ) {
if (tMaxX > radius) break;
+ // Update which cube we are now in.
x += stepX;
+ // Adjust tMaxX to the next X-oriented boundary crossing.
tMaxX += tDeltaX;
+ // Record the normal vector of the cube face we entered.
face[0] = -stepX;
face[1] = 0;
face[2] = 0;
@@ -380,6 +408,8 @@
face[1] = -stepY;
face[2] = 0;
} else {
+ // Identical to the second case, repeated for simplicity in
+ // the conditionals.
if (tMaxZ > radius) break;
z += stepZ;
tMaxZ += tDeltaZ;

0 comments on commit 89d5e6d

Please sign in to comment.