Wed, 31 Dec 2014 13:27:57 +0100
Ignore runtime configuration files generated during quality assurance.
1 /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 "use strict";
8 /**
9 * This worker handles picking, given a set of vertices and a ray (calculates
10 * the intersection points and offers back information about the closest hit).
11 *
12 * Used in the TiltVisualization.Presenter object.
13 */
14 self.onmessage = function TWP_onMessage(event)
15 {
16 let data = event.data;
17 let vertices = data.vertices;
18 let ray = data.ray;
20 let intersection = null;
21 let hit = [];
23 // calculates the squared distance between two points
24 function dsq(p1, p2) {
25 let xd = p2[0] - p1[0];
26 let yd = p2[1] - p1[1];
27 let zd = p2[2] - p1[2];
29 return xd * xd + yd * yd + zd * zd;
30 }
32 // check each stack face in the visualization mesh for intersections with
33 // the mouse ray (using a ray picking algorithm)
34 for (let i = 0, len = vertices.length; i < len; i += 36) {
36 // the front quad
37 let v0f = [vertices[i], vertices[i + 1], vertices[i + 2]];
38 let v1f = [vertices[i + 3], vertices[i + 4], vertices[i + 5]];
39 let v2f = [vertices[i + 6], vertices[i + 7], vertices[i + 8]];
40 let v3f = [vertices[i + 9], vertices[i + 10], vertices[i + 11]];
42 // the back quad
43 let v0b = [vertices[i + 24], vertices[i + 25], vertices[i + 26]];
44 let v1b = [vertices[i + 27], vertices[i + 28], vertices[i + 29]];
45 let v2b = [vertices[i + 30], vertices[i + 31], vertices[i + 32]];
46 let v3b = [vertices[i + 33], vertices[i + 34], vertices[i + 35]];
48 // don't do anything with degenerate quads
49 if (!v0f[0] && !v1f[0] && !v2f[0] && !v3f[0]) {
50 continue;
51 }
53 // for each triangle in the stack box, check for the intersections
54 if (self.intersect(v0f, v1f, v2f, ray, hit) || // front left
55 self.intersect(v0f, v2f, v3f, ray, hit) || // front right
56 self.intersect(v0b, v1b, v1f, ray, hit) || // left back
57 self.intersect(v0b, v1f, v0f, ray, hit) || // left front
58 self.intersect(v3f, v2b, v3b, ray, hit) || // right back
59 self.intersect(v3f, v2f, v2b, ray, hit) || // right front
60 self.intersect(v0b, v0f, v3f, ray, hit) || // top left
61 self.intersect(v0b, v3f, v3b, ray, hit) || // top right
62 self.intersect(v1f, v1b, v2b, ray, hit) || // bottom left
63 self.intersect(v1f, v2b, v2f, ray, hit)) { // bottom right
65 // calculate the distance between the intersection hit point and camera
66 let d = dsq(hit, ray.origin);
68 // we're picking the closest stack in the mesh from the camera
69 if (intersection === null || d < intersection.distance) {
70 intersection = {
71 // each mesh stack is composed of 12 vertices, so there's information
72 // about a node once in 12 * 3 = 36 iterations (to avoid duplication)
73 index: i / 36,
74 distance: d
75 };
76 }
77 }
78 }
80 self.postMessage(intersection);
81 close();
82 };
84 /**
85 * Utility function for finding intersections between a ray and a triangle.
86 */
87 self.intersect = (function() {
89 // creates a new instance of a vector
90 function create() {
91 return new Float32Array(3);
92 }
94 // performs a vector addition
95 function add(aVec, aVec2, aDest) {
96 aDest[0] = aVec[0] + aVec2[0];
97 aDest[1] = aVec[1] + aVec2[1];
98 aDest[2] = aVec[2] + aVec2[2];
99 return aDest;
100 }
102 // performs a vector subtraction
103 function subtract(aVec, aVec2, aDest) {
104 aDest[0] = aVec[0] - aVec2[0];
105 aDest[1] = aVec[1] - aVec2[1];
106 aDest[2] = aVec[2] - aVec2[2];
107 return aDest;
108 }
110 // performs a vector scaling
111 function scale(aVec, aVal, aDest) {
112 aDest[0] = aVec[0] * aVal;
113 aDest[1] = aVec[1] * aVal;
114 aDest[2] = aVec[2] * aVal;
115 return aDest;
116 }
118 // generates the cross product of two vectors
119 function cross(aVec, aVec2, aDest) {
120 let x = aVec[0];
121 let y = aVec[1];
122 let z = aVec[2];
123 let x2 = aVec2[0];
124 let y2 = aVec2[1];
125 let z2 = aVec2[2];
127 aDest[0] = y * z2 - z * y2;
128 aDest[1] = z * x2 - x * z2;
129 aDest[2] = x * y2 - y * x2;
130 return aDest;
131 }
133 // calculates the dot product of two vectors
134 function dot(aVec, aVec2) {
135 return aVec[0] * aVec2[0] + aVec[1] * aVec2[1] + aVec[2] * aVec2[2];
136 }
138 let edge1 = create();
139 let edge2 = create();
140 let pvec = create();
141 let tvec = create();
142 let qvec = create();
143 let lvec = create();
145 // checks for ray-triangle intersections using the Fast Minimum-Storage
146 // (simplified) algorithm by Tomas Moller and Ben Trumbore
147 return function intersect(aVert0, aVert1, aVert2, aRay, aDest) {
148 let dir = aRay.direction;
149 let orig = aRay.origin;
151 // find vectors for two edges sharing vert0
152 subtract(aVert1, aVert0, edge1);
153 subtract(aVert2, aVert0, edge2);
155 // begin calculating determinant - also used to calculate the U parameter
156 cross(dir, edge2, pvec);
158 // if determinant is near zero, ray lines in plane of triangle
159 let inv_det = 1 / dot(edge1, pvec);
161 // calculate distance from vert0 to ray origin
162 subtract(orig, aVert0, tvec);
164 // calculate U parameter and test bounds
165 let u = dot(tvec, pvec) * inv_det;
166 if (u < 0 || u > 1) {
167 return false;
168 }
170 // prepare to test V parameter
171 cross(tvec, edge1, qvec);
173 // calculate V parameter and test bounds
174 let v = dot(dir, qvec) * inv_det;
175 if (v < 0 || u + v > 1) {
176 return false;
177 }
179 // calculate T, ray intersects triangle
180 let t = dot(edge2, qvec) * inv_det;
182 scale(dir, t, lvec);
183 add(orig, lvec, aDest);
184 return true;
185 };
186 }());