michael@0: /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: "use strict"; michael@0: michael@0: /** michael@0: * This worker handles picking, given a set of vertices and a ray (calculates michael@0: * the intersection points and offers back information about the closest hit). michael@0: * michael@0: * Used in the TiltVisualization.Presenter object. michael@0: */ michael@0: self.onmessage = function TWP_onMessage(event) michael@0: { michael@0: let data = event.data; michael@0: let vertices = data.vertices; michael@0: let ray = data.ray; michael@0: michael@0: let intersection = null; michael@0: let hit = []; michael@0: michael@0: // calculates the squared distance between two points michael@0: function dsq(p1, p2) { michael@0: let xd = p2[0] - p1[0]; michael@0: let yd = p2[1] - p1[1]; michael@0: let zd = p2[2] - p1[2]; michael@0: michael@0: return xd * xd + yd * yd + zd * zd; michael@0: } michael@0: michael@0: // check each stack face in the visualization mesh for intersections with michael@0: // the mouse ray (using a ray picking algorithm) michael@0: for (let i = 0, len = vertices.length; i < len; i += 36) { michael@0: michael@0: // the front quad michael@0: let v0f = [vertices[i], vertices[i + 1], vertices[i + 2]]; michael@0: let v1f = [vertices[i + 3], vertices[i + 4], vertices[i + 5]]; michael@0: let v2f = [vertices[i + 6], vertices[i + 7], vertices[i + 8]]; michael@0: let v3f = [vertices[i + 9], vertices[i + 10], vertices[i + 11]]; michael@0: michael@0: // the back quad michael@0: let v0b = [vertices[i + 24], vertices[i + 25], vertices[i + 26]]; michael@0: let v1b = [vertices[i + 27], vertices[i + 28], vertices[i + 29]]; michael@0: let v2b = [vertices[i + 30], vertices[i + 31], vertices[i + 32]]; michael@0: let v3b = [vertices[i + 33], vertices[i + 34], vertices[i + 35]]; michael@0: michael@0: // don't do anything with degenerate quads michael@0: if (!v0f[0] && !v1f[0] && !v2f[0] && !v3f[0]) { michael@0: continue; michael@0: } michael@0: michael@0: // for each triangle in the stack box, check for the intersections michael@0: if (self.intersect(v0f, v1f, v2f, ray, hit) || // front left michael@0: self.intersect(v0f, v2f, v3f, ray, hit) || // front right michael@0: self.intersect(v0b, v1b, v1f, ray, hit) || // left back michael@0: self.intersect(v0b, v1f, v0f, ray, hit) || // left front michael@0: self.intersect(v3f, v2b, v3b, ray, hit) || // right back michael@0: self.intersect(v3f, v2f, v2b, ray, hit) || // right front michael@0: self.intersect(v0b, v0f, v3f, ray, hit) || // top left michael@0: self.intersect(v0b, v3f, v3b, ray, hit) || // top right michael@0: self.intersect(v1f, v1b, v2b, ray, hit) || // bottom left michael@0: self.intersect(v1f, v2b, v2f, ray, hit)) { // bottom right michael@0: michael@0: // calculate the distance between the intersection hit point and camera michael@0: let d = dsq(hit, ray.origin); michael@0: michael@0: // we're picking the closest stack in the mesh from the camera michael@0: if (intersection === null || d < intersection.distance) { michael@0: intersection = { michael@0: // each mesh stack is composed of 12 vertices, so there's information michael@0: // about a node once in 12 * 3 = 36 iterations (to avoid duplication) michael@0: index: i / 36, michael@0: distance: d michael@0: }; michael@0: } michael@0: } michael@0: } michael@0: michael@0: self.postMessage(intersection); michael@0: close(); michael@0: }; michael@0: michael@0: /** michael@0: * Utility function for finding intersections between a ray and a triangle. michael@0: */ michael@0: self.intersect = (function() { michael@0: michael@0: // creates a new instance of a vector michael@0: function create() { michael@0: return new Float32Array(3); michael@0: } michael@0: michael@0: // performs a vector addition michael@0: function add(aVec, aVec2, aDest) { michael@0: aDest[0] = aVec[0] + aVec2[0]; michael@0: aDest[1] = aVec[1] + aVec2[1]; michael@0: aDest[2] = aVec[2] + aVec2[2]; michael@0: return aDest; michael@0: } michael@0: michael@0: // performs a vector subtraction michael@0: function subtract(aVec, aVec2, aDest) { michael@0: aDest[0] = aVec[0] - aVec2[0]; michael@0: aDest[1] = aVec[1] - aVec2[1]; michael@0: aDest[2] = aVec[2] - aVec2[2]; michael@0: return aDest; michael@0: } michael@0: michael@0: // performs a vector scaling michael@0: function scale(aVec, aVal, aDest) { michael@0: aDest[0] = aVec[0] * aVal; michael@0: aDest[1] = aVec[1] * aVal; michael@0: aDest[2] = aVec[2] * aVal; michael@0: return aDest; michael@0: } michael@0: michael@0: // generates the cross product of two vectors michael@0: function cross(aVec, aVec2, aDest) { michael@0: let x = aVec[0]; michael@0: let y = aVec[1]; michael@0: let z = aVec[2]; michael@0: let x2 = aVec2[0]; michael@0: let y2 = aVec2[1]; michael@0: let z2 = aVec2[2]; michael@0: michael@0: aDest[0] = y * z2 - z * y2; michael@0: aDest[1] = z * x2 - x * z2; michael@0: aDest[2] = x * y2 - y * x2; michael@0: return aDest; michael@0: } michael@0: michael@0: // calculates the dot product of two vectors michael@0: function dot(aVec, aVec2) { michael@0: return aVec[0] * aVec2[0] + aVec[1] * aVec2[1] + aVec[2] * aVec2[2]; michael@0: } michael@0: michael@0: let edge1 = create(); michael@0: let edge2 = create(); michael@0: let pvec = create(); michael@0: let tvec = create(); michael@0: let qvec = create(); michael@0: let lvec = create(); michael@0: michael@0: // checks for ray-triangle intersections using the Fast Minimum-Storage michael@0: // (simplified) algorithm by Tomas Moller and Ben Trumbore michael@0: return function intersect(aVert0, aVert1, aVert2, aRay, aDest) { michael@0: let dir = aRay.direction; michael@0: let orig = aRay.origin; michael@0: michael@0: // find vectors for two edges sharing vert0 michael@0: subtract(aVert1, aVert0, edge1); michael@0: subtract(aVert2, aVert0, edge2); michael@0: michael@0: // begin calculating determinant - also used to calculate the U parameter michael@0: cross(dir, edge2, pvec); michael@0: michael@0: // if determinant is near zero, ray lines in plane of triangle michael@0: let inv_det = 1 / dot(edge1, pvec); michael@0: michael@0: // calculate distance from vert0 to ray origin michael@0: subtract(orig, aVert0, tvec); michael@0: michael@0: // calculate U parameter and test bounds michael@0: let u = dot(tvec, pvec) * inv_det; michael@0: if (u < 0 || u > 1) { michael@0: return false; michael@0: } michael@0: michael@0: // prepare to test V parameter michael@0: cross(tvec, edge1, qvec); michael@0: michael@0: // calculate V parameter and test bounds michael@0: let v = dot(dir, qvec) * inv_det; michael@0: if (v < 0 || u + v > 1) { michael@0: return false; michael@0: } michael@0: michael@0: // calculate T, ray intersects triangle michael@0: let t = dot(edge2, qvec) * inv_det; michael@0: michael@0: scale(dir, t, lvec); michael@0: add(orig, lvec, aDest); michael@0: return true; michael@0: }; michael@0: }());