var Vec3 = require('./Vec3');
var EPSILON = 0.0001;
var Vec3 = require('./Vec3');
var EPSILON = 0.0001;
A ray.
Consists of the starting point origin and the direction vector.
Used for collision detection.
function Ray(origin, direction) {
this.origin = origin || new Vec3(0, 0, 0);
this.direction = direction || new Vec3(0, 0, 1);
}
Ray.prototype.hitTestSphere = function (pos, r) {
var hits = [];
var d = this.direction;
var o = this.origin;
var osp = o.dup().sub(pos);
var A = d.dot(d);
if (A == 0) {
return hits;
}
var B = 2 * osp.dot(d);
var C = osp.dot(osp) - r * r;
var sq = Math.sqrt(B * B - 4 * A * C);
if (isNaN(sq)) {
return hits;
}
var t0 = (-B - sq) / (2 * A);
var t1 = (-B + sq) / (2 * A);
hits.push(o.dup().add(d.dup().scale(t0)));
if (t0 != t1) {
hits.push(o.dup().add(d.dup().scale(t1)));
}
return hits;
};
http://www.cs.princeton.edu/courses/archive/fall00/cs426/lectures/raycast/sld017.htm http://cgafaq.info/wiki/Ray_Plane_Intersection
Ray.prototype.hitTestPlane = function (pos, normal) {
if (this.direction.dot(normal) == 0) {
return [];
}
var t = normal.dup().scale(-1).dot(this.origin.dup().sub(pos)) / this.direction.dot(normal);
return [this.origin.dup().add(this.direction.dup().scale(t))];
};
Ray.prototype.hitTestBoundingBox = function (bbox) {
var hits = [];
var self = this;
function testFace(pos, size, normal, u, v) {
var faceHits = self.hitTestPlane(pos, normal);
if (faceHits.length > 0) {
var hit = faceHits[0];
if (hit[u] > pos[u] - size[u] / 2 && hit[u] < pos[u] + size[u] / 2 && hit[v] > pos[v] - size[v] / 2 && hit[v] < pos[v] + size[v] / 2) {
hits.push(hit);
}
}
}
var bboxCenter = bbox.getCenter();
var bboxSize = bbox.getSize();
testFace(bboxCenter.dup().add(new Vec3(0, 0, bboxSize.z / 2)), bboxSize, new Vec3(0, 0, 1), 'x', 'y');
testFace(bboxCenter.dup().add(new Vec3(0, 0, -bboxSize.z / 2)), bboxSize, new Vec3(0, 0, -1), 'x', 'y');
testFace(bboxCenter.dup().add(new Vec3(bboxSize.x / 2, 0, 0)), bboxSize, new Vec3(1, 0, 0), 'y', 'z');
testFace(bboxCenter.dup().add(new Vec3(-bboxSize.x / 2, 0, 0)), bboxSize, new Vec3(-1, 0, 0), 'y', 'z');
testFace(bboxCenter.dup().add(new Vec3(0, bboxSize.y / 2, 0)), bboxSize, new Vec3(0, 1, 0), 'x', 'z');
testFace(bboxCenter.dup().add(new Vec3(0, -bboxSize.y / 2, 0)), bboxSize, new Vec3(0, -1, 0), 'x', 'z');
hits.forEach(function (hit) {
hit._distance = hit.distance(self.origin);
});
hits.sort(function (a, b) {
return a._distance - b._distance;
});
hits.forEach(function (hit) {
delete hit._distance;
});
if (hits.length > 0) {
hits = [hits[0]];
}
return hits;
};
Ray.prototype.hitTestTriangle = function(triangle) {
Vector u, v, n; // triangle vectors Vector dir, w0, w; // ray vectors float r, a, b; // params to calc ray-plane intersect
var ray = this;
// get triangle edge vectors and plane normal u = T.V1 - T.V0; v = T.V2 - T.V0;
var u = triangle.b.dup().sub(triangle.a);
var v = triangle.c.dup().sub(triangle.a);
n = u * v; // cross product
var n = Vec3.create().asCross(u, v);
if (n == (Vector)0) // triangle is degenerate return -1; // do not deal with this case
if (n.length() < EPSILON) return -1;
dir = R.P1 - R.P0; // ray direction vector w0 = R.P0 - T.V0;
var w0 = ray.origin.dup().sub(triangle.a);
a = -dot(n,w0); b = dot(n,dir);
var a = -n.dot(w0);
var b = n.dot(ray.direction);
if (fabs(b) < SMALL_NUM) { // ray is parallel to triangle plane if (a == 0) // ray lies in triangle plane return 2; else return 0; // ray disjoint from plane }
if (Math.abs(b) < EPSILON) {
if (a == 0) return -2;
else return -3;
}
// get intersect point of ray with triangle plane r = a / b; if (r < 0.0) // ray goes away from triangle return 0; // => no intersect // for a segment, also test if (r > 1.0) => no intersect
var r = a / b;
if (r < -EPSILON) {
return -4;
}
I = R.P0 + r dir; // intersect point of ray and plane
var I = ray.origin.dup().add(ray.direction.dup().scale(r));
// is I inside T? float uu, uv, vv, wu, wv, D; uu = dot(u,u); uv = dot(u,v); vv = dot(v,v);
var uu = u.dot(u);
var uv = u.dot(v);
var vv = v.dot(v);
w = *I - T.V0;
var w = I.dup().sub(triangle.a);
wu = dot(w,u); wv = dot(w,v);
var wu = w.dot(u);
var wv = w.dot(v);
D = uv uv - uu vv;
var D = uv * uv - uu * vv;
// get and test parametric coords float s, t; s = (uv wv - vv wu) / D;
var s = (uv * wv - vv * wu) / D;
if (s < 0.0 || s > 1.0) // I is outside T return 0;
if (s < -EPSILON || s > 1.0 + EPSILON) return -5;
t = (uv wu - uu wv) / D;
var t = (uv * wu - uu * wv) / D;
if (t < 0.0 || (s + t) > 1.0) // I is outside T return 0;
if (t < -EPSILON || (s + t) > 1.0 + EPSILON) {
return -6;
}
return { s: s, t : t}; // I is in T
return u.scale(s).add(v.scale(t)).add(triangle.a);
}
module.exports = Ray;