|  | @@ -19,7 +19,7 @@ module.exports = {
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{"./src/controls":84,"./src/loaders":93,"./src/misc":101,"./src/pathfinding":107,"./src/primitives":115,"aframe-physics-system":11}],3:[function(require,module,exports){
 | 
	
		
			
				|  |  | +},{"./src/controls":89,"./src/loaders":97,"./src/misc":104,"./src/pathfinding":110,"./src/primitives":118,"aframe-physics-system":11}],3:[function(require,module,exports){
 | 
	
		
			
				|  |  |  /**
 | 
	
		
			
				|  |  |   * @author Kyle-Larson https://github.com/Kyle-Larson
 | 
	
		
			
				|  |  |   * @author Takahiro https://github.com/takahirox
 | 
	
	
		
			
				|  | @@ -6986,7 +6986,7 @@ module.exports = {
 | 
	
		
			
				|  |  |    }())
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{"../../../lib/CANNON-shape2mesh":12,"cannon":23,"three-to-cannon":79}],14:[function(require,module,exports){
 | 
	
		
			
				|  |  | +},{"../../../lib/CANNON-shape2mesh":12,"cannon":23,"three-to-cannon":84}],14:[function(require,module,exports){
 | 
	
		
			
				|  |  |  var Body = require('./body');
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /**
 | 
	
	
		
			
				|  | @@ -21920,4733 +21920,4483 @@ World.prototype.clearForces = function(){
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  },{"../collision/AABB":24,"../collision/ArrayCollisionMatrix":25,"../collision/NaiveBroadphase":28,"../collision/OverlapKeeper":30,"../collision/Ray":31,"../collision/RaycastResult":32,"../equations/ContactEquation":41,"../equations/FrictionEquation":43,"../material/ContactMaterial":46,"../material/Material":47,"../math/Quaternion":50,"../math/Vec3":52,"../objects/Body":53,"../shapes/Shape":65,"../solver/GSSolver":68,"../utils/EventTarget":71,"../utils/TupleDictionary":74,"./Narrowphase":77}],79:[function(require,module,exports){
 | 
	
		
			
				|  |  | -var CANNON = require('cannon'),
 | 
	
		
			
				|  |  | -    quickhull = require('./lib/THREE.quickhull');
 | 
	
		
			
				|  |  | +const BinaryHeap = require('./BinaryHeap');
 | 
	
		
			
				|  |  | +const utils = require('./utils.js');
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -var PI_2 = Math.PI / 2;
 | 
	
		
			
				|  |  | +class AStar {
 | 
	
		
			
				|  |  | +  static init (graph) {
 | 
	
		
			
				|  |  | +    for (let x = 0; x < graph.length; x++) {
 | 
	
		
			
				|  |  | +      //for(var x in graph) {
 | 
	
		
			
				|  |  | +      const node = graph[x];
 | 
	
		
			
				|  |  | +      node.f = 0;
 | 
	
		
			
				|  |  | +      node.g = 0;
 | 
	
		
			
				|  |  | +      node.h = 0;
 | 
	
		
			
				|  |  | +      node.cost = 1.0;
 | 
	
		
			
				|  |  | +      node.visited = false;
 | 
	
		
			
				|  |  | +      node.closed = false;
 | 
	
		
			
				|  |  | +      node.parent = null;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -var Type = {
 | 
	
		
			
				|  |  | -  BOX: 'Box',
 | 
	
		
			
				|  |  | -  CYLINDER: 'Cylinder',
 | 
	
		
			
				|  |  | -  SPHERE: 'Sphere',
 | 
	
		
			
				|  |  | -  HULL: 'ConvexPolyhedron',
 | 
	
		
			
				|  |  | -  MESH: 'Trimesh'
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | +  static cleanUp (graph) {
 | 
	
		
			
				|  |  | +    for (let x = 0; x < graph.length; x++) {
 | 
	
		
			
				|  |  | +      const node = graph[x];
 | 
	
		
			
				|  |  | +      delete node.f;
 | 
	
		
			
				|  |  | +      delete node.g;
 | 
	
		
			
				|  |  | +      delete node.h;
 | 
	
		
			
				|  |  | +      delete node.cost;
 | 
	
		
			
				|  |  | +      delete node.visited;
 | 
	
		
			
				|  |  | +      delete node.closed;
 | 
	
		
			
				|  |  | +      delete node.parent;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * Given a THREE.Object3D instance, creates a corresponding CANNON shape.
 | 
	
		
			
				|  |  | - * @param  {THREE.Object3D} object
 | 
	
		
			
				|  |  | - * @return {CANNON.Shape}
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -module.exports = CANNON.mesh2shape = function (object, options) {
 | 
	
		
			
				|  |  | -  options = options || {};
 | 
	
		
			
				|  |  | +  static heap () {
 | 
	
		
			
				|  |  | +    return new BinaryHeap(function (node) {
 | 
	
		
			
				|  |  | +      return node.f;
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  var geometry;
 | 
	
		
			
				|  |  | +  static search (graph, start, end) {
 | 
	
		
			
				|  |  | +    this.init(graph);
 | 
	
		
			
				|  |  | +    //heuristic = heuristic || astar.manhattan;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  if (options.type === Type.BOX) {
 | 
	
		
			
				|  |  | -    return createBoundingBoxShape(object);
 | 
	
		
			
				|  |  | -  } else if (options.type === Type.CYLINDER) {
 | 
	
		
			
				|  |  | -    return createBoundingCylinderShape(object, options);
 | 
	
		
			
				|  |  | -  } else if (options.type === Type.SPHERE) {
 | 
	
		
			
				|  |  | -    return createBoundingSphereShape(object, options);
 | 
	
		
			
				|  |  | -  } else if (options.type === Type.HULL) {
 | 
	
		
			
				|  |  | -    return createConvexPolyhedron(object);
 | 
	
		
			
				|  |  | -  } else if (options.type === Type.MESH) {
 | 
	
		
			
				|  |  | -    geometry = getGeometry(object);
 | 
	
		
			
				|  |  | -    return geometry ? createTrimeshShape(geometry) : null;
 | 
	
		
			
				|  |  | -  } else if (options.type) {
 | 
	
		
			
				|  |  | -    throw new Error('[CANNON.mesh2shape] Invalid type "%s".', options.type);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  geometry = getGeometry(object);
 | 
	
		
			
				|  |  | -  if (!geometry) return null;
 | 
	
		
			
				|  |  | +    const openHeap = this.heap();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  var type = geometry.metadata
 | 
	
		
			
				|  |  | -    ? geometry.metadata.type
 | 
	
		
			
				|  |  | -    : geometry.type;
 | 
	
		
			
				|  |  | +    openHeap.push(start);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  switch (type) {
 | 
	
		
			
				|  |  | -    case 'BoxGeometry':
 | 
	
		
			
				|  |  | -    case 'BoxBufferGeometry':
 | 
	
		
			
				|  |  | -      return createBoxShape(geometry);
 | 
	
		
			
				|  |  | -    case 'CylinderGeometry':
 | 
	
		
			
				|  |  | -    case 'CylinderBufferGeometry':
 | 
	
		
			
				|  |  | -      return createCylinderShape(geometry);
 | 
	
		
			
				|  |  | -    case 'PlaneGeometry':
 | 
	
		
			
				|  |  | -    case 'PlaneBufferGeometry':
 | 
	
		
			
				|  |  | -      return createPlaneShape(geometry);
 | 
	
		
			
				|  |  | -    case 'SphereGeometry':
 | 
	
		
			
				|  |  | -    case 'SphereBufferGeometry':
 | 
	
		
			
				|  |  | -      return createSphereShape(geometry);
 | 
	
		
			
				|  |  | -    case 'TubeGeometry':
 | 
	
		
			
				|  |  | -    case 'Geometry':
 | 
	
		
			
				|  |  | -    case 'BufferGeometry':
 | 
	
		
			
				|  |  | -      return createBoundingBoxShape(object);
 | 
	
		
			
				|  |  | -    default:
 | 
	
		
			
				|  |  | -      console.warn('Unrecognized geometry: "%s". Using bounding box as shape.', geometry.type);
 | 
	
		
			
				|  |  | -      return createBoxShape(geometry);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | +    while (openHeap.size() > 0) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -CANNON.mesh2shape.Type = Type;
 | 
	
		
			
				|  |  | +      // Grab the lowest f(x) to process next.  Heap keeps this sorted for us.
 | 
	
		
			
				|  |  | +      const currentNode = openHeap.pop();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -/******************************************************************************
 | 
	
		
			
				|  |  | - * Shape construction
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | +      // End case -- result has been found, return the traced path.
 | 
	
		
			
				|  |  | +      if (currentNode === end) {
 | 
	
		
			
				|  |  | +        let curr = currentNode;
 | 
	
		
			
				|  |  | +        const ret = [];
 | 
	
		
			
				|  |  | +        while (curr.parent) {
 | 
	
		
			
				|  |  | +          ret.push(curr);
 | 
	
		
			
				|  |  | +          curr = curr.parent;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        this.cleanUp(ret);
 | 
	
		
			
				|  |  | +        return ret.reverse();
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | - /**
 | 
	
		
			
				|  |  | -  * @param  {THREE.Geometry} geometry
 | 
	
		
			
				|  |  | -  * @return {CANNON.Shape}
 | 
	
		
			
				|  |  | -  */
 | 
	
		
			
				|  |  | - function createBoxShape (geometry) {
 | 
	
		
			
				|  |  | -   var vertices = getVertices(geometry);
 | 
	
		
			
				|  |  | +      // Normal case -- move currentNode from open to closed, process each of its neighbours.
 | 
	
		
			
				|  |  | +      currentNode.closed = true;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -   if (!vertices.length) return null;
 | 
	
		
			
				|  |  | +      // Find all neighbours for the current node. Optionally find diagonal neighbours as well (false by default).
 | 
	
		
			
				|  |  | +      const neighbours = this.neighbours(graph, currentNode);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -   geometry.computeBoundingBox();
 | 
	
		
			
				|  |  | -   var box = geometry.boundingBox;
 | 
	
		
			
				|  |  | -   return new CANNON.Box(new CANNON.Vec3(
 | 
	
		
			
				|  |  | -     (box.max.x - box.min.x) / 2,
 | 
	
		
			
				|  |  | -     (box.max.y - box.min.y) / 2,
 | 
	
		
			
				|  |  | -     (box.max.z - box.min.z) / 2
 | 
	
		
			
				|  |  | -   ));
 | 
	
		
			
				|  |  | - }
 | 
	
		
			
				|  |  | +      for (let i = 0, il = neighbours.length; i < il; i++) {
 | 
	
		
			
				|  |  | +        const neighbour = neighbours[i];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * Bounding box needs to be computed with the entire mesh, not just geometry.
 | 
	
		
			
				|  |  | - * @param  {THREE.Object3D} mesh
 | 
	
		
			
				|  |  | - * @return {CANNON.Shape}
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -function createBoundingBoxShape (object) {
 | 
	
		
			
				|  |  | -  var shape, localPosition, worldPosition,
 | 
	
		
			
				|  |  | -      box = new THREE.Box3();
 | 
	
		
			
				|  |  | +        if (neighbour.closed) {
 | 
	
		
			
				|  |  | +          // Not a valid node to process, skip to next neighbour.
 | 
	
		
			
				|  |  | +          continue;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  box.setFromObject(object);
 | 
	
		
			
				|  |  | +        // The g score is the shortest distance from start to current node.
 | 
	
		
			
				|  |  | +        // We need to check if the path we have arrived at this neighbour is the shortest one we have seen yet.
 | 
	
		
			
				|  |  | +        const gScore = currentNode.g + neighbour.cost;
 | 
	
		
			
				|  |  | +        const beenVisited = neighbour.visited;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  if (!isFinite(box.min.lengthSq())) return null;
 | 
	
		
			
				|  |  | +        if (!beenVisited || gScore < neighbour.g) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  shape = new CANNON.Box(new CANNON.Vec3(
 | 
	
		
			
				|  |  | -    (box.max.x - box.min.x) / 2,
 | 
	
		
			
				|  |  | -    (box.max.y - box.min.y) / 2,
 | 
	
		
			
				|  |  | -    (box.max.z - box.min.z) / 2
 | 
	
		
			
				|  |  | -  ));
 | 
	
		
			
				|  |  | +          // Found an optimal (so far) path to this node.  Take score for node to see how good it is.
 | 
	
		
			
				|  |  | +          neighbour.visited = true;
 | 
	
		
			
				|  |  | +          neighbour.parent = currentNode;
 | 
	
		
			
				|  |  | +          if (!neighbour.centroid || !end.centroid) throw new Error('Unexpected state');
 | 
	
		
			
				|  |  | +          neighbour.h = neighbour.h || this.heuristic(neighbour.centroid, end.centroid);
 | 
	
		
			
				|  |  | +          neighbour.g = gScore;
 | 
	
		
			
				|  |  | +          neighbour.f = neighbour.g + neighbour.h;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  object.updateMatrixWorld();
 | 
	
		
			
				|  |  | -  worldPosition = new THREE.Vector3();
 | 
	
		
			
				|  |  | -  worldPosition.setFromMatrixPosition(object.matrixWorld);
 | 
	
		
			
				|  |  | -  localPosition = box.translate(worldPosition.negate()).getCenter();
 | 
	
		
			
				|  |  | -  if (localPosition.lengthSq()) {
 | 
	
		
			
				|  |  | -    shape.offset = localPosition;
 | 
	
		
			
				|  |  | +          if (!beenVisited) {
 | 
	
		
			
				|  |  | +            // Pushing to heap will put it in proper place based on the 'f' value.
 | 
	
		
			
				|  |  | +            openHeap.push(neighbour);
 | 
	
		
			
				|  |  | +          } else {
 | 
	
		
			
				|  |  | +            // Already seen the node, but since it has been rescored we need to reorder it in the heap
 | 
	
		
			
				|  |  | +            openHeap.rescoreElement(neighbour);
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // No result was found - empty array signifies failure to find path.
 | 
	
		
			
				|  |  | +    return [];
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  return shape;
 | 
	
		
			
				|  |  | +  static heuristic (pos1, pos2) {
 | 
	
		
			
				|  |  | +    return utils.distanceToSquared(pos1, pos2);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  static neighbours (graph, node) {
 | 
	
		
			
				|  |  | +    const ret = [];
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    for (let e = 0; e < node.neighbours.length; e++) {
 | 
	
		
			
				|  |  | +      ret.push(graph[node.neighbours[e]]);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    return ret;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * Computes 3D convex hull as a CANNON.ConvexPolyhedron.
 | 
	
		
			
				|  |  | - * @param  {THREE.Object3D} mesh
 | 
	
		
			
				|  |  | - * @return {CANNON.Shape}
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -function createConvexPolyhedron (object) {
 | 
	
		
			
				|  |  | -  var i, vertices, faces, hull,
 | 
	
		
			
				|  |  | -      eps = 1e-4,
 | 
	
		
			
				|  |  | -      geometry = getGeometry(object);
 | 
	
		
			
				|  |  | +module.exports = AStar;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  if (!geometry || !geometry.vertices.length) return null;
 | 
	
		
			
				|  |  | +},{"./BinaryHeap":80,"./utils.js":83}],80:[function(require,module,exports){
 | 
	
		
			
				|  |  | +// javascript-astar
 | 
	
		
			
				|  |  | +// http://github.com/bgrins/javascript-astar
 | 
	
		
			
				|  |  | +// Freely distributable under the MIT License.
 | 
	
		
			
				|  |  | +// Implements the astar search algorithm in javascript using a binary heap.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  // Perturb.
 | 
	
		
			
				|  |  | -  for (i = 0; i < geometry.vertices.length; i++) {
 | 
	
		
			
				|  |  | -    geometry.vertices[i].x += (Math.random() - 0.5) * eps;
 | 
	
		
			
				|  |  | -    geometry.vertices[i].y += (Math.random() - 0.5) * eps;
 | 
	
		
			
				|  |  | -    geometry.vertices[i].z += (Math.random() - 0.5) * eps;
 | 
	
		
			
				|  |  | +class BinaryHeap {
 | 
	
		
			
				|  |  | +  constructor (scoreFunction) {
 | 
	
		
			
				|  |  | +    this.content = [];
 | 
	
		
			
				|  |  | +    this.scoreFunction = scoreFunction;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  // Compute the 3D convex hull.
 | 
	
		
			
				|  |  | -  hull = quickhull(geometry);
 | 
	
		
			
				|  |  | +  push (element) {
 | 
	
		
			
				|  |  | +    // Add the new element to the end of the array.
 | 
	
		
			
				|  |  | +    this.content.push(element);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  // Convert from THREE.Vector3 to CANNON.Vec3.
 | 
	
		
			
				|  |  | -  vertices = new Array(hull.vertices.length);
 | 
	
		
			
				|  |  | -  for (i = 0; i < hull.vertices.length; i++) {
 | 
	
		
			
				|  |  | -    vertices[i] = new CANNON.Vec3(hull.vertices[i].x, hull.vertices[i].y, hull.vertices[i].z);
 | 
	
		
			
				|  |  | +    // Allow it to sink down.
 | 
	
		
			
				|  |  | +    this.sinkDown(this.content.length - 1);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  // Convert from THREE.Face to Array<number>.
 | 
	
		
			
				|  |  | -  faces = new Array(hull.faces.length);
 | 
	
		
			
				|  |  | -  for (i = 0; i < hull.faces.length; i++) {
 | 
	
		
			
				|  |  | -    faces[i] = [hull.faces[i].a, hull.faces[i].b, hull.faces[i].c];
 | 
	
		
			
				|  |  | +  pop () {
 | 
	
		
			
				|  |  | +    // Store the first element so we can return it later.
 | 
	
		
			
				|  |  | +    const result = this.content[0];
 | 
	
		
			
				|  |  | +    // Get the element at the end of the array.
 | 
	
		
			
				|  |  | +    const end = this.content.pop();
 | 
	
		
			
				|  |  | +    // If there are any elements left, put the end element at the
 | 
	
		
			
				|  |  | +    // start, and let it bubble up.
 | 
	
		
			
				|  |  | +    if (this.content.length > 0) {
 | 
	
		
			
				|  |  | +      this.content[0] = end;
 | 
	
		
			
				|  |  | +      this.bubbleUp(0);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    return result;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  return new CANNON.ConvexPolyhedron(vertices, faces);
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | +  remove (node) {
 | 
	
		
			
				|  |  | +    const i = this.content.indexOf(node);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * @param  {THREE.Geometry} geometry
 | 
	
		
			
				|  |  | - * @return {CANNON.Shape}
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -function createCylinderShape (geometry) {
 | 
	
		
			
				|  |  | -  var shape,
 | 
	
		
			
				|  |  | -      params = geometry.metadata
 | 
	
		
			
				|  |  | -        ? geometry.metadata.parameters
 | 
	
		
			
				|  |  | -        : geometry.parameters;
 | 
	
		
			
				|  |  | -  shape = new CANNON.Cylinder(
 | 
	
		
			
				|  |  | -    params.radiusTop,
 | 
	
		
			
				|  |  | -    params.radiusBottom,
 | 
	
		
			
				|  |  | -    params.height,
 | 
	
		
			
				|  |  | -    params.radialSegments
 | 
	
		
			
				|  |  | -  );
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  // Include metadata for serialization.
 | 
	
		
			
				|  |  | -  shape._type = CANNON.Shape.types.CYLINDER; // Patch schteppe/cannon.js#329.
 | 
	
		
			
				|  |  | -  shape.radiusTop = params.radiusTop;
 | 
	
		
			
				|  |  | -  shape.radiusBottom = params.radiusBottom;
 | 
	
		
			
				|  |  | -  shape.height = params.height;
 | 
	
		
			
				|  |  | -  shape.numSegments = params.radialSegments;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  shape.orientation = new CANNON.Quaternion();
 | 
	
		
			
				|  |  | -  shape.orientation.setFromEuler(THREE.Math.degToRad(-90), 0, 0, 'XYZ').normalize();
 | 
	
		
			
				|  |  | -  return shape;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * @param  {THREE.Object3D} object
 | 
	
		
			
				|  |  | - * @return {CANNON.Shape}
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -function createBoundingCylinderShape (object, options) {
 | 
	
		
			
				|  |  | -  var shape, height, radius,
 | 
	
		
			
				|  |  | -      box = new THREE.Box3(),
 | 
	
		
			
				|  |  | -      axes = ['x', 'y', 'z'],
 | 
	
		
			
				|  |  | -      majorAxis = options.cylinderAxis || 'y',
 | 
	
		
			
				|  |  | -      minorAxes = axes.splice(axes.indexOf(majorAxis), 1) && axes;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  box.setFromObject(object);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  if (!isFinite(box.min.lengthSq())) return null;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  // Compute cylinder dimensions.
 | 
	
		
			
				|  |  | -  height = box.max[majorAxis] - box.min[majorAxis];
 | 
	
		
			
				|  |  | -  radius = 0.5 * Math.max(
 | 
	
		
			
				|  |  | -    box.max[minorAxes[0]] - box.min[minorAxes[0]],
 | 
	
		
			
				|  |  | -    box.max[minorAxes[1]] - box.min[minorAxes[1]]
 | 
	
		
			
				|  |  | -  );
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  // Create shape.
 | 
	
		
			
				|  |  | -  shape = new CANNON.Cylinder(radius, radius, height, 12);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  // Include metadata for serialization.
 | 
	
		
			
				|  |  | -  shape._type = CANNON.Shape.types.CYLINDER; // Patch schteppe/cannon.js#329.
 | 
	
		
			
				|  |  | -  shape.radiusTop = radius;
 | 
	
		
			
				|  |  | -  shape.radiusBottom = radius;
 | 
	
		
			
				|  |  | -  shape.height = height;
 | 
	
		
			
				|  |  | -  shape.numSegments = 12;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  shape.orientation = new CANNON.Quaternion();
 | 
	
		
			
				|  |  | -  shape.orientation.setFromEuler(
 | 
	
		
			
				|  |  | -    majorAxis === 'y' ? PI_2 : 0,
 | 
	
		
			
				|  |  | -    majorAxis === 'z' ? PI_2 : 0,
 | 
	
		
			
				|  |  | -    0,
 | 
	
		
			
				|  |  | -    'XYZ'
 | 
	
		
			
				|  |  | -  ).normalize();
 | 
	
		
			
				|  |  | -  return shape;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * @param  {THREE.Geometry} geometry
 | 
	
		
			
				|  |  | - * @return {CANNON.Shape}
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -function createPlaneShape (geometry) {
 | 
	
		
			
				|  |  | -  geometry.computeBoundingBox();
 | 
	
		
			
				|  |  | -  var box = geometry.boundingBox;
 | 
	
		
			
				|  |  | -  return new CANNON.Box(new CANNON.Vec3(
 | 
	
		
			
				|  |  | -    (box.max.x - box.min.x) / 2 || 0.1,
 | 
	
		
			
				|  |  | -    (box.max.y - box.min.y) / 2 || 0.1,
 | 
	
		
			
				|  |  | -    (box.max.z - box.min.z) / 2 || 0.1
 | 
	
		
			
				|  |  | -  ));
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * @param  {THREE.Geometry} geometry
 | 
	
		
			
				|  |  | - * @return {CANNON.Shape}
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -function createSphereShape (geometry) {
 | 
	
		
			
				|  |  | -  var params = geometry.metadata
 | 
	
		
			
				|  |  | -    ? geometry.metadata.parameters
 | 
	
		
			
				|  |  | -    : geometry.parameters;
 | 
	
		
			
				|  |  | -  return new CANNON.Sphere(params.radius);
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * @param  {THREE.Object3D} object
 | 
	
		
			
				|  |  | - * @return {CANNON.Shape}
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -function createBoundingSphereShape (object, options) {
 | 
	
		
			
				|  |  | -  if (options.sphereRadius) {
 | 
	
		
			
				|  |  | -    return new CANNON.Sphere(options.sphereRadius);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  var geometry = getGeometry(object);
 | 
	
		
			
				|  |  | -  if (!geometry) return null;
 | 
	
		
			
				|  |  | -  geometry.computeBoundingSphere();
 | 
	
		
			
				|  |  | -  return new CANNON.Sphere(geometry.boundingSphere.radius);
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * @param  {THREE.Geometry} geometry
 | 
	
		
			
				|  |  | - * @return {CANNON.Shape}
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -function createTrimeshShape (geometry) {
 | 
	
		
			
				|  |  | -  var indices,
 | 
	
		
			
				|  |  | -      vertices = getVertices(geometry);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  if (!vertices.length) return null;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  indices = Object.keys(vertices).map(Number);
 | 
	
		
			
				|  |  | -  return new CANNON.Trimesh(vertices, indices);
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -/******************************************************************************
 | 
	
		
			
				|  |  | - * Utils
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * Returns a single geometry for the given object. If the object is compound,
 | 
	
		
			
				|  |  | - * its geometries are automatically merged.
 | 
	
		
			
				|  |  | - * @param {THREE.Object3D} object
 | 
	
		
			
				|  |  | - * @return {THREE.Geometry}
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -function getGeometry (object) {
 | 
	
		
			
				|  |  | -  var matrix, mesh,
 | 
	
		
			
				|  |  | -      meshes = getMeshes(object),
 | 
	
		
			
				|  |  | -      tmp = new THREE.Geometry(),
 | 
	
		
			
				|  |  | -      combined = new THREE.Geometry();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  if (meshes.length === 0) return null;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  // Apply scale  – it can't easily be applied to a CANNON.Shape later.
 | 
	
		
			
				|  |  | -  if (meshes.length === 1) {
 | 
	
		
			
				|  |  | -    var position = new THREE.Vector3(),
 | 
	
		
			
				|  |  | -        quaternion = new THREE.Quaternion(),
 | 
	
		
			
				|  |  | -        scale = new THREE.Vector3();
 | 
	
		
			
				|  |  | -    if (meshes[0].geometry instanceof THREE.BufferGeometry) {
 | 
	
		
			
				|  |  | -      if (meshes[0].geometry.attributes.position) {
 | 
	
		
			
				|  |  | -        tmp.fromBufferGeometry(meshes[0].geometry);
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -    } else {
 | 
	
		
			
				|  |  | -      tmp = meshes[0].geometry.clone();
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    tmp.metadata = meshes[0].geometry.metadata;
 | 
	
		
			
				|  |  | -    meshes[0].updateMatrixWorld();
 | 
	
		
			
				|  |  | -    meshes[0].matrixWorld.decompose(position, quaternion, scale);
 | 
	
		
			
				|  |  | -    return tmp.scale(scale.x, scale.y, scale.z);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  // Recursively merge geometry, preserving local transforms.
 | 
	
		
			
				|  |  | -  while ((mesh = meshes.pop())) {
 | 
	
		
			
				|  |  | -    mesh.updateMatrixWorld();
 | 
	
		
			
				|  |  | -    if (mesh.geometry instanceof THREE.BufferGeometry) {
 | 
	
		
			
				|  |  | -      tmp.fromBufferGeometry(mesh.geometry);
 | 
	
		
			
				|  |  | -      combined.merge(tmp, mesh.matrixWorld);
 | 
	
		
			
				|  |  | -    } else {
 | 
	
		
			
				|  |  | -      combined.merge(mesh.geometry, mesh.matrixWorld);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  matrix = new THREE.Matrix4();
 | 
	
		
			
				|  |  | -  matrix.scale(object.scale);
 | 
	
		
			
				|  |  | -  combined.applyMatrix(matrix);
 | 
	
		
			
				|  |  | -  return combined;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * @param  {THREE.Geometry} geometry
 | 
	
		
			
				|  |  | - * @return {Array<number>}
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -function getVertices (geometry) {
 | 
	
		
			
				|  |  | -  if (!geometry.attributes) {
 | 
	
		
			
				|  |  | -    geometry = new THREE.BufferGeometry().fromGeometry(geometry);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  return (geometry.attributes.position || {}).array || [];
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * Returns a flat array of THREE.Mesh instances from the given object. If
 | 
	
		
			
				|  |  | - * nested transformations are found, they are applied to child meshes
 | 
	
		
			
				|  |  | - * as mesh.userData.matrix, so that each mesh has its position/rotation/scale
 | 
	
		
			
				|  |  | - * independently of all of its parents except the top-level object.
 | 
	
		
			
				|  |  | - * @param  {THREE.Object3D} object
 | 
	
		
			
				|  |  | - * @return {Array<THREE.Mesh>}
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -function getMeshes (object) {
 | 
	
		
			
				|  |  | -  var meshes = [];
 | 
	
		
			
				|  |  | -  object.traverse(function (o) {
 | 
	
		
			
				|  |  | -    if (o.type === 'Mesh') {
 | 
	
		
			
				|  |  | -      meshes.push(o);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  });
 | 
	
		
			
				|  |  | -  return meshes;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -},{"./lib/THREE.quickhull":80,"cannon":23}],80:[function(require,module,exports){
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  QuickHull
 | 
	
		
			
				|  |  | -  ---------
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  The MIT License
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  Copyright © 2010-2014 three.js authors
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
	
		
			
				|  |  | -  of this software and associated documentation files (the "Software"), to deal
 | 
	
		
			
				|  |  | -  in the Software without restriction, including without limitation the rights
 | 
	
		
			
				|  |  | -  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
	
		
			
				|  |  | -  copies of the Software, and to permit persons to whom the Software is
 | 
	
		
			
				|  |  | -  furnished to do so, subject to the following conditions:
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  The above copyright notice and this permission notice shall be included in
 | 
	
		
			
				|  |  | -  all copies or substantial portions of the Software.
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
	
		
			
				|  |  | -  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
	
		
			
				|  |  | -  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
	
		
			
				|  |  | -  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
	
		
			
				|  |  | -  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
	
		
			
				|  |  | -  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  THE SOFTWARE.
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    @author mark lundin / http://mark-lundin.com
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    This is a 3D implementation of the Quick Hull algorithm.
 | 
	
		
			
				|  |  | -    It is a fast way of computing a convex hull with average complexity
 | 
	
		
			
				|  |  | -    of O(n log(n)).
 | 
	
		
			
				|  |  | -    It uses depends on three.js and is supposed to create THREE.Geometry.
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    It's also very messy
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -module.exports = (function(){
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  var faces     = [],
 | 
	
		
			
				|  |  | -    faceStack   = [],
 | 
	
		
			
				|  |  | -    i, NUM_POINTS, extremes,
 | 
	
		
			
				|  |  | -    max     = 0,
 | 
	
		
			
				|  |  | -    dcur, current, j, v0, v1, v2, v3,
 | 
	
		
			
				|  |  | -    N, D;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  var ab, ac, ax,
 | 
	
		
			
				|  |  | -    suba, subb, normal,
 | 
	
		
			
				|  |  | -    diff, subaA, subaB, subC;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  function reset(){
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    ab    = new THREE.Vector3(),
 | 
	
		
			
				|  |  | -    ac    = new THREE.Vector3(),
 | 
	
		
			
				|  |  | -    ax    = new THREE.Vector3(),
 | 
	
		
			
				|  |  | -    suba  = new THREE.Vector3(),
 | 
	
		
			
				|  |  | -    subb  = new THREE.Vector3(),
 | 
	
		
			
				|  |  | -    normal  = new THREE.Vector3(),
 | 
	
		
			
				|  |  | -    diff  = new THREE.Vector3(),
 | 
	
		
			
				|  |  | -    subaA = new THREE.Vector3(),
 | 
	
		
			
				|  |  | -    subaB = new THREE.Vector3(),
 | 
	
		
			
				|  |  | -    subC  = new THREE.Vector3();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  //temporary vectors
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  function process( points ){
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    // Iterate through all the faces and remove
 | 
	
		
			
				|  |  | -    while( faceStack.length > 0  ){
 | 
	
		
			
				|  |  | -      cull( faceStack.shift(), points );
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  var norm = function(){
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    var ca = new THREE.Vector3(),
 | 
	
		
			
				|  |  | -      ba = new THREE.Vector3(),
 | 
	
		
			
				|  |  | -      N = new THREE.Vector3();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    return function( a, b, c ){
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      ca.subVectors( c, a );
 | 
	
		
			
				|  |  | -      ba.subVectors( b, a );
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      N.crossVectors( ca, ba );
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      return N.normalize();
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  }();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  function getNormal( face, points ){
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    if( face.normal !== undefined ) return face.normal;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    var p0 = points[face[0]],
 | 
	
		
			
				|  |  | -      p1 = points[face[1]],
 | 
	
		
			
				|  |  | -      p2 = points[face[2]];
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    ab.subVectors( p1, p0 );
 | 
	
		
			
				|  |  | -    ac.subVectors( p2, p0 );
 | 
	
		
			
				|  |  | -    normal.crossVectors( ac, ab );
 | 
	
		
			
				|  |  | -    normal.normalize();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    return face.normal = normal.clone();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  function assignPoints( face, pointset, points ){
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    // ASSIGNING POINTS TO FACE
 | 
	
		
			
				|  |  | -    var p0 = points[face[0]],
 | 
	
		
			
				|  |  | -      dots = [], apex,
 | 
	
		
			
				|  |  | -      norm = getNormal( face, points );
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    // Sory all the points by there distance from the plane
 | 
	
		
			
				|  |  | -    pointset.sort( function( aItem, bItem ){
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      dots[aItem.x/3] = dots[aItem.x/3] !== undefined ? dots[aItem.x/3] : norm.dot( suba.subVectors( aItem, p0 ));
 | 
	
		
			
				|  |  | -      dots[bItem.x/3] = dots[bItem.x/3] !== undefined ? dots[bItem.x/3] : norm.dot( subb.subVectors( bItem, p0 ));
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      return dots[aItem.x/3] - dots[bItem.x/3] ;
 | 
	
		
			
				|  |  | -    });
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    //TODO :: Must be a faster way of finding and index in this array
 | 
	
		
			
				|  |  | -    var index = pointset.length;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    if( index === 1 ) dots[pointset[0].x/3] = norm.dot( suba.subVectors( pointset[0], p0 ));
 | 
	
		
			
				|  |  | -    while( index-- > 0 && dots[pointset[index].x/3] > 0 )
 | 
	
		
			
				|  |  | +    // When it is found, the process seen in 'pop' is repeated
 | 
	
		
			
				|  |  | +    // to fill up the hole.
 | 
	
		
			
				|  |  | +    const end = this.content.pop();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    var point;
 | 
	
		
			
				|  |  | -    if( index + 1 < pointset.length && dots[pointset[index+1].x/3] > 0 ){
 | 
	
		
			
				|  |  | +    if (i !== this.content.length - 1) {
 | 
	
		
			
				|  |  | +      this.content[i] = end;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      face.visiblePoints  = pointset.splice( index + 1 );
 | 
	
		
			
				|  |  | +      if (this.scoreFunction(end) < this.scoreFunction(node)) {
 | 
	
		
			
				|  |  | +        this.sinkDown(i);
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        this.bubbleUp(i);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  size () {
 | 
	
		
			
				|  |  | +    return this.content.length;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  rescoreElement (node) {
 | 
	
		
			
				|  |  | +    this.sinkDown(this.content.indexOf(node));
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  sinkDown (n) {
 | 
	
		
			
				|  |  | +    // Fetch the element that has to be sunk.
 | 
	
		
			
				|  |  | +    const element = this.content[n];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  function cull( face, points ){
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    var i = faces.length,
 | 
	
		
			
				|  |  | -      dot, visibleFace, currentFace,
 | 
	
		
			
				|  |  | -      visibleFaces = [face];
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    var apex = points.indexOf( face.visiblePoints.pop() );
 | 
	
		
			
				|  |  | +    // When at 0, an element can not sink any further.
 | 
	
		
			
				|  |  | +    while (n > 0) {
 | 
	
		
			
				|  |  | +      // Compute the parent element's index, and fetch it.
 | 
	
		
			
				|  |  | +      const parentN = ((n + 1) >> 1) - 1;
 | 
	
		
			
				|  |  | +      const parent = this.content[parentN];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // Iterate through all other faces...
 | 
	
		
			
				|  |  | -    while( i-- > 0 ){
 | 
	
		
			
				|  |  | -      currentFace = faces[i];
 | 
	
		
			
				|  |  | -      if( currentFace !== face ){
 | 
	
		
			
				|  |  | -        // ...and check if they're pointing in the same direction
 | 
	
		
			
				|  |  | -        dot = getNormal( currentFace, points ).dot( diff.subVectors( points[apex], points[currentFace[0]] ));
 | 
	
		
			
				|  |  | -        if( dot > 0 ){
 | 
	
		
			
				|  |  | -          visibleFaces.push( currentFace );
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | +      if (this.scoreFunction(element) < this.scoreFunction(parent)) {
 | 
	
		
			
				|  |  | +        // Swap the elements if the parent is greater.
 | 
	
		
			
				|  |  | +        this.content[parentN] = element;
 | 
	
		
			
				|  |  | +        this.content[n] = parent;
 | 
	
		
			
				|  |  | +        // Update 'n' to continue at the new position.
 | 
	
		
			
				|  |  | +        n = parentN;
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        // Found a parent that is less, no need to sink any further.
 | 
	
		
			
				|  |  | +        break;
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    var index, neighbouringIndex, vertex;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    // Determine Perimeter - Creates a bounded horizon
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    // 1. Pick an edge A out of all possible edges
 | 
	
		
			
				|  |  | -    // 2. Check if A is shared by any other face. a->b === b->a
 | 
	
		
			
				|  |  | -      // 2.1 for each edge in each triangle, isShared = ( f1.a == f2.a && f1.b == f2.b ) || ( f1.a == f2.b && f1.b == f2.a )
 | 
	
		
			
				|  |  | -    // 3. If not shared, then add to convex horizon set,
 | 
	
		
			
				|  |  | -        //pick an end point (N) of the current edge A and choose a new edge NA connected to A.
 | 
	
		
			
				|  |  | -        //Restart from 1.
 | 
	
		
			
				|  |  | -    // 4. If A is shared, it is not an horizon edge, therefore flag both faces that share this edge as candidates for culling
 | 
	
		
			
				|  |  | -    // 5. If candidate geometry is a degenrate triangle (ie. the tangent space normal cannot be computed) then remove that triangle from all further processing
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    var j = i = visibleFaces.length;
 | 
	
		
			
				|  |  | -    var isDistinct = false,
 | 
	
		
			
				|  |  | -      hasOneVisibleFace = i === 1,
 | 
	
		
			
				|  |  | -      cull = [],
 | 
	
		
			
				|  |  | -      perimeter = [],
 | 
	
		
			
				|  |  | -      edgeIndex = 0, compareFace, nextIndex,
 | 
	
		
			
				|  |  | -      a, b;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    var allPoints = [];
 | 
	
		
			
				|  |  | -    var originFace = [visibleFaces[0][0], visibleFaces[0][1], visibleFaces[0][1], visibleFaces[0][2], visibleFaces[0][2], visibleFaces[0][0]];
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | +  bubbleUp (n) {
 | 
	
		
			
				|  |  | +    // Look up the target element and its score.
 | 
	
		
			
				|  |  | +    const length = this.content.length,
 | 
	
		
			
				|  |  | +      element = this.content[n],
 | 
	
		
			
				|  |  | +      elemScore = this.scoreFunction(element);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if( visibleFaces.length === 1 ){
 | 
	
		
			
				|  |  | -      currentFace = visibleFaces[0];
 | 
	
		
			
				|  |  | +    while (true) {
 | 
	
		
			
				|  |  | +      // Compute the indices of the child elements.
 | 
	
		
			
				|  |  | +      const child2N = (n + 1) << 1,
 | 
	
		
			
				|  |  | +        child1N = child2N - 1;
 | 
	
		
			
				|  |  | +      // This is used to store the new position of the element,
 | 
	
		
			
				|  |  | +      // if any.
 | 
	
		
			
				|  |  | +      let swap = null;
 | 
	
		
			
				|  |  | +      let child1Score;
 | 
	
		
			
				|  |  | +      // If the first child exists (is inside the array)...
 | 
	
		
			
				|  |  | +      if (child1N < length) {
 | 
	
		
			
				|  |  | +        // Look it up and compute its score.
 | 
	
		
			
				|  |  | +        const child1 = this.content[child1N];
 | 
	
		
			
				|  |  | +        child1Score = this.scoreFunction(child1);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      perimeter = [currentFace[0], currentFace[1], currentFace[1], currentFace[2], currentFace[2], currentFace[0]];
 | 
	
		
			
				|  |  | -      // remove visible face from list of faces
 | 
	
		
			
				|  |  | -      if( faceStack.indexOf( currentFace ) > -1 ){
 | 
	
		
			
				|  |  | -        faceStack.splice( faceStack.indexOf( currentFace ), 1 );
 | 
	
		
			
				|  |  | +        // If the score is less than our element's, we need to swap.
 | 
	
		
			
				|  |  | +        if (child1Score < elemScore) {
 | 
	
		
			
				|  |  | +          swap = child1N;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      if( currentFace.visiblePoints ) allPoints = allPoints.concat( currentFace.visiblePoints );
 | 
	
		
			
				|  |  | -      faces.splice( faces.indexOf( currentFace ), 1 );
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    }else{
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      while( i-- > 0  ){  // for each visible face
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        currentFace = visibleFaces[i];
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        // remove visible face from list of faces
 | 
	
		
			
				|  |  | -        if( faceStack.indexOf( currentFace ) > -1 ){
 | 
	
		
			
				|  |  | -          faceStack.splice( faceStack.indexOf( currentFace ), 1 );
 | 
	
		
			
				|  |  | +      // Do the same checks for the other child.
 | 
	
		
			
				|  |  | +      if (child2N < length) {
 | 
	
		
			
				|  |  | +        const child2 = this.content[child2N],
 | 
	
		
			
				|  |  | +          child2Score = this.scoreFunction(child2);
 | 
	
		
			
				|  |  | +        if (child2Score < (swap === null ? elemScore : child1Score)) {
 | 
	
		
			
				|  |  | +          swap = child2N;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        if( currentFace.visiblePoints ) allPoints = allPoints.concat( currentFace.visiblePoints );
 | 
	
		
			
				|  |  | -        faces.splice( faces.indexOf( currentFace ), 1 );
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | +      // If the element needs to be moved, swap it, and continue.
 | 
	
		
			
				|  |  | +      if (swap !== null) {
 | 
	
		
			
				|  |  | +        this.content[n] = this.content[swap];
 | 
	
		
			
				|  |  | +        this.content[swap] = element;
 | 
	
		
			
				|  |  | +        n = swap;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        var isSharedEdge;
 | 
	
		
			
				|  |  | -        cEdgeIndex = 0;
 | 
	
		
			
				|  |  | +      // Otherwise, we are done.
 | 
	
		
			
				|  |  | +      else {
 | 
	
		
			
				|  |  | +        break;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        while( cEdgeIndex < 3 ){ // Iterate through it's edges
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -          isSharedEdge = false;
 | 
	
		
			
				|  |  | -          j = visibleFaces.length;
 | 
	
		
			
				|  |  | -          a = currentFace[cEdgeIndex]
 | 
	
		
			
				|  |  | -          b = currentFace[(cEdgeIndex+1)%3];
 | 
	
		
			
				|  |  | +module.exports = BinaryHeap;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +},{}],81:[function(require,module,exports){
 | 
	
		
			
				|  |  | +const utils = require('./utils');
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -          while( j-- > 0 && !isSharedEdge ){ // find another visible faces
 | 
	
		
			
				|  |  | +class Channel {
 | 
	
		
			
				|  |  | +  constructor () {
 | 
	
		
			
				|  |  | +    this.portals = [];
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            compareFace = visibleFaces[j];
 | 
	
		
			
				|  |  | -            edgeIndex = 0;
 | 
	
		
			
				|  |  | +  push (p1, p2) {
 | 
	
		
			
				|  |  | +    if (p2 === undefined) p2 = p1;
 | 
	
		
			
				|  |  | +    this.portals.push({
 | 
	
		
			
				|  |  | +      left: p1,
 | 
	
		
			
				|  |  | +      right: p2
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            // isSharedEdge = compareFace == currentFace;
 | 
	
		
			
				|  |  | -            if( compareFace !== currentFace ){
 | 
	
		
			
				|  |  | +  stringPull () {
 | 
	
		
			
				|  |  | +    const portals = this.portals;
 | 
	
		
			
				|  |  | +    const pts = [];
 | 
	
		
			
				|  |  | +    // Init scan state
 | 
	
		
			
				|  |  | +    let portalApex, portalLeft, portalRight;
 | 
	
		
			
				|  |  | +    let apexIndex = 0,
 | 
	
		
			
				|  |  | +      leftIndex = 0,
 | 
	
		
			
				|  |  | +      rightIndex = 0;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -              while( edgeIndex < 3 && !isSharedEdge ){ //Check all it's indices
 | 
	
		
			
				|  |  | +    portalApex = portals[0].left;
 | 
	
		
			
				|  |  | +    portalLeft = portals[0].left;
 | 
	
		
			
				|  |  | +    portalRight = portals[0].right;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                nextIndex = ( edgeIndex + 1 );
 | 
	
		
			
				|  |  | -                isSharedEdge = ( compareFace[edgeIndex] === a && compareFace[nextIndex%3] === b ) ||
 | 
	
		
			
				|  |  | -                         ( compareFace[edgeIndex] === b && compareFace[nextIndex%3] === a );
 | 
	
		
			
				|  |  | +    // Add start point.
 | 
	
		
			
				|  |  | +    pts.push(portalApex);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                edgeIndex++;
 | 
	
		
			
				|  |  | -              }
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -          }
 | 
	
		
			
				|  |  | +    for (let i = 1; i < portals.length; i++) {
 | 
	
		
			
				|  |  | +      const left = portals[i].left;
 | 
	
		
			
				|  |  | +      const right = portals[i].right;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -          if( !isSharedEdge || hasOneVisibleFace ){
 | 
	
		
			
				|  |  | -            perimeter.push( a );
 | 
	
		
			
				|  |  | -            perimeter.push( b );
 | 
	
		
			
				|  |  | -          }
 | 
	
		
			
				|  |  | +      // Update right vertex.
 | 
	
		
			
				|  |  | +      if (utils.triarea2(portalApex, portalRight, right) <= 0.0) {
 | 
	
		
			
				|  |  | +        if (utils.vequal(portalApex, portalRight) || utils.triarea2(portalApex, portalLeft, right) > 0.0) {
 | 
	
		
			
				|  |  | +          // Tighten the funnel.
 | 
	
		
			
				|  |  | +          portalRight = right;
 | 
	
		
			
				|  |  | +          rightIndex = i;
 | 
	
		
			
				|  |  | +        } else {
 | 
	
		
			
				|  |  | +          // Right over left, insert left to path and restart scan from portal left point.
 | 
	
		
			
				|  |  | +          pts.push(portalLeft);
 | 
	
		
			
				|  |  | +          // Make current left the new apex.
 | 
	
		
			
				|  |  | +          portalApex = portalLeft;
 | 
	
		
			
				|  |  | +          apexIndex = leftIndex;
 | 
	
		
			
				|  |  | +          // Reset portal
 | 
	
		
			
				|  |  | +          portalLeft = portalApex;
 | 
	
		
			
				|  |  | +          portalRight = portalApex;
 | 
	
		
			
				|  |  | +          leftIndex = apexIndex;
 | 
	
		
			
				|  |  | +          rightIndex = apexIndex;
 | 
	
		
			
				|  |  | +          // Restart scan
 | 
	
		
			
				|  |  | +          i = apexIndex;
 | 
	
		
			
				|  |  | +          continue;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -          cEdgeIndex++;
 | 
	
		
			
				|  |  | +      // Update left vertex.
 | 
	
		
			
				|  |  | +      if (utils.triarea2(portalApex, portalLeft, left) >= 0.0) {
 | 
	
		
			
				|  |  | +        if (utils.vequal(portalApex, portalLeft) || utils.triarea2(portalApex, portalRight, left) < 0.0) {
 | 
	
		
			
				|  |  | +          // Tighten the funnel.
 | 
	
		
			
				|  |  | +          portalLeft = left;
 | 
	
		
			
				|  |  | +          leftIndex = i;
 | 
	
		
			
				|  |  | +        } else {
 | 
	
		
			
				|  |  | +          // Left over right, insert right to path and restart scan from portal right point.
 | 
	
		
			
				|  |  | +          pts.push(portalRight);
 | 
	
		
			
				|  |  | +          // Make current right the new apex.
 | 
	
		
			
				|  |  | +          portalApex = portalRight;
 | 
	
		
			
				|  |  | +          apexIndex = rightIndex;
 | 
	
		
			
				|  |  | +          // Reset portal
 | 
	
		
			
				|  |  | +          portalLeft = portalApex;
 | 
	
		
			
				|  |  | +          portalRight = portalApex;
 | 
	
		
			
				|  |  | +          leftIndex = apexIndex;
 | 
	
		
			
				|  |  | +          rightIndex = apexIndex;
 | 
	
		
			
				|  |  | +          // Restart scan
 | 
	
		
			
				|  |  | +          i = apexIndex;
 | 
	
		
			
				|  |  | +          continue;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // create new face for all pairs around edge
 | 
	
		
			
				|  |  | -    i = 0;
 | 
	
		
			
				|  |  | -    var l = perimeter.length/2;
 | 
	
		
			
				|  |  | -    var f;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    while( i < l ){
 | 
	
		
			
				|  |  | -      f = [ perimeter[i*2+1], apex, perimeter[i*2] ];
 | 
	
		
			
				|  |  | -      assignPoints( f, allPoints, points );
 | 
	
		
			
				|  |  | -      faces.push( f )
 | 
	
		
			
				|  |  | -      if( f.visiblePoints !== undefined  )faceStack.push( f );
 | 
	
		
			
				|  |  | -      i++;
 | 
	
		
			
				|  |  | +    if ((pts.length === 0) || (!utils.vequal(pts[pts.length - 1], portals[portals.length - 1].left))) {
 | 
	
		
			
				|  |  | +      // Append last point to path.
 | 
	
		
			
				|  |  | +      pts.push(portals[portals.length - 1].left);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    this.path = pts;
 | 
	
		
			
				|  |  | +    return pts;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  var distSqPointSegment = function(){
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    var ab = new THREE.Vector3(),
 | 
	
		
			
				|  |  | -      ac = new THREE.Vector3(),
 | 
	
		
			
				|  |  | -      bc = new THREE.Vector3();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    return function( a, b, c ){
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        ab.subVectors( b, a );
 | 
	
		
			
				|  |  | -        ac.subVectors( c, a );
 | 
	
		
			
				|  |  | -        bc.subVectors( c, b );
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        var e = ac.dot(ab);
 | 
	
		
			
				|  |  | -        if (e < 0.0) return ac.dot( ac );
 | 
	
		
			
				|  |  | -        var f = ab.dot( ab );
 | 
	
		
			
				|  |  | -        if (e >= f) return bc.dot(  bc );
 | 
	
		
			
				|  |  | -        return ac.dot( ac ) - e * e / f;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  }();
 | 
	
		
			
				|  |  | +module.exports = Channel;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +},{"./utils":83}],82:[function(require,module,exports){
 | 
	
		
			
				|  |  | +const utils = require('./utils');
 | 
	
		
			
				|  |  | +const AStar = require('./AStar');
 | 
	
		
			
				|  |  | +const Channel = require('./Channel');
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +var polygonId = 1;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +var buildPolygonGroups = function (navigationMesh) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	var polygons = navigationMesh.polygons;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  return function( geometry ){
 | 
	
		
			
				|  |  | +	var polygonGroups = [];
 | 
	
		
			
				|  |  | +	var groupCount = 0;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    reset();
 | 
	
		
			
				|  |  | +	var spreadGroupId = function (polygon) {
 | 
	
		
			
				|  |  | +		polygon.neighbours.forEach((neighbour) => {
 | 
	
		
			
				|  |  | +			if (neighbour.group === undefined) {
 | 
	
		
			
				|  |  | +				neighbour.group = polygon.group;
 | 
	
		
			
				|  |  | +				spreadGroupId(neighbour);
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +		});
 | 
	
		
			
				|  |  | +	};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	polygons.forEach((polygon) => {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    points    = geometry.vertices;
 | 
	
		
			
				|  |  | -    faces     = [],
 | 
	
		
			
				|  |  | -    faceStack   = [],
 | 
	
		
			
				|  |  | -    i       = NUM_POINTS = points.length,
 | 
	
		
			
				|  |  | -    extremes  = points.slice( 0, 6 ),
 | 
	
		
			
				|  |  | -    max     = 0;
 | 
	
		
			
				|  |  | +		if (polygon.group === undefined) {
 | 
	
		
			
				|  |  | +			polygon.group = groupCount++;
 | 
	
		
			
				|  |  | +			// Spread it
 | 
	
		
			
				|  |  | +			spreadGroupId(polygon);
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +		if (!polygonGroups[polygon.group]) polygonGroups[polygon.group] = [];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +		polygonGroups[polygon.group].push(polygon);
 | 
	
		
			
				|  |  | +	});
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    /*
 | 
	
		
			
				|  |  | -     *  FIND EXTREMETIES
 | 
	
		
			
				|  |  | -     */
 | 
	
		
			
				|  |  | -    while( i-- > 0 ){
 | 
	
		
			
				|  |  | -      if( points[i].x < extremes[0].x ) extremes[0] = points[i];
 | 
	
		
			
				|  |  | -      if( points[i].x > extremes[1].x ) extremes[1] = points[i];
 | 
	
		
			
				|  |  | +	console.log('Groups built: ', polygonGroups.length);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      if( points[i].y < extremes[2].y ) extremes[2] = points[i];
 | 
	
		
			
				|  |  | -      if( points[i].y < extremes[3].y ) extremes[3] = points[i];
 | 
	
		
			
				|  |  | +	return polygonGroups;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      if( points[i].z < extremes[4].z ) extremes[4] = points[i];
 | 
	
		
			
				|  |  | -      if( points[i].z < extremes[5].z ) extremes[5] = points[i];
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +var buildPolygonNeighbours = function (polygon, navigationMesh) {
 | 
	
		
			
				|  |  | +	polygon.neighbours = [];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	// All other nodes that contain at least two of our vertices are our neighbours
 | 
	
		
			
				|  |  | +	for (var i = 0, len = navigationMesh.polygons.length; i < len; i++) {
 | 
	
		
			
				|  |  | +		if (polygon === navigationMesh.polygons[i]) continue;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    /*
 | 
	
		
			
				|  |  | -     *  Find the longest line between the extremeties
 | 
	
		
			
				|  |  | -     */
 | 
	
		
			
				|  |  | +		// Don't check polygons that are too far, since the intersection tests take a long time
 | 
	
		
			
				|  |  | +		if (polygon.centroid.distanceToSquared(navigationMesh.polygons[i].centroid) > 100 * 100) continue;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    j = i = 6;
 | 
	
		
			
				|  |  | -    while( i-- > 0 ){
 | 
	
		
			
				|  |  | -      j = i - 1;
 | 
	
		
			
				|  |  | -      while( j-- > 0 ){
 | 
	
		
			
				|  |  | -          if( max < (dcur = extremes[i].distanceToSquared( extremes[j] )) ){
 | 
	
		
			
				|  |  | -        max = dcur;
 | 
	
		
			
				|  |  | -        v0 = extremes[ i ];
 | 
	
		
			
				|  |  | -        v1 = extremes[ j ];
 | 
	
		
			
				|  |  | +		var matches = utils.array_intersect(polygon.vertexIds, navigationMesh.polygons[i].vertexIds);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -          }
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | +		if (matches.length >= 2) {
 | 
	
		
			
				|  |  | +			polygon.neighbours.push(navigationMesh.polygons[i]);
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +var buildPolygonsFromGeometry = function (geometry) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      // 3. Find the most distant point to the line segment, this creates a plane
 | 
	
		
			
				|  |  | -      i = 6;
 | 
	
		
			
				|  |  | -      max = 0;
 | 
	
		
			
				|  |  | -    while( i-- > 0 ){
 | 
	
		
			
				|  |  | -      dcur = distSqPointSegment( v0, v1, extremes[i]);
 | 
	
		
			
				|  |  | -      if( max < dcur ){
 | 
	
		
			
				|  |  | -        max = dcur;
 | 
	
		
			
				|  |  | -            v2 = extremes[ i ];
 | 
	
		
			
				|  |  | -          }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +	console.log('Vertices:', geometry.vertices.length, 'polygons:', geometry.faces.length);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	var polygons = [];
 | 
	
		
			
				|  |  | +	var vertices = geometry.vertices;
 | 
	
		
			
				|  |  | +	var faceVertexUvs = geometry.faceVertexUvs;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      // 4. Find the most distant point to the plane.
 | 
	
		
			
				|  |  | +	// Convert the faces into a custom format that supports more than 3 vertices
 | 
	
		
			
				|  |  | +	geometry.faces.forEach((face) => {
 | 
	
		
			
				|  |  | +		polygons.push({
 | 
	
		
			
				|  |  | +			id: polygonId++,
 | 
	
		
			
				|  |  | +			vertexIds: [face.a, face.b, face.c],
 | 
	
		
			
				|  |  | +			centroid: face.centroid,
 | 
	
		
			
				|  |  | +			normal: face.normal,
 | 
	
		
			
				|  |  | +			neighbours: []
 | 
	
		
			
				|  |  | +		});
 | 
	
		
			
				|  |  | +	});
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      N = norm(v0, v1, v2);
 | 
	
		
			
				|  |  | -      D = N.dot( v0 );
 | 
	
		
			
				|  |  | +	var navigationMesh = {
 | 
	
		
			
				|  |  | +		polygons: polygons,
 | 
	
		
			
				|  |  | +		vertices: vertices,
 | 
	
		
			
				|  |  | +		faceVertexUvs: faceVertexUvs
 | 
	
		
			
				|  |  | +	};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	// Build a list of adjacent polygons
 | 
	
		
			
				|  |  | +	polygons.forEach((polygon) => {
 | 
	
		
			
				|  |  | +		buildPolygonNeighbours(polygon, navigationMesh);
 | 
	
		
			
				|  |  | +	});
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      max = 0;
 | 
	
		
			
				|  |  | -      i = NUM_POINTS;
 | 
	
		
			
				|  |  | -      while( i-- > 0 ){
 | 
	
		
			
				|  |  | -        dcur = Math.abs( points[i].dot( N ) - D );
 | 
	
		
			
				|  |  | -          if( max < dcur ){
 | 
	
		
			
				|  |  | -            max = dcur;
 | 
	
		
			
				|  |  | -            v3 = points[i];
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | +	return navigationMesh;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +var buildNavigationMesh = function (geometry) {
 | 
	
		
			
				|  |  | +	// Prepare geometry
 | 
	
		
			
				|  |  | +	utils.computeCentroids(geometry);
 | 
	
		
			
				|  |  | +	geometry.mergeVertices();
 | 
	
		
			
				|  |  | +	return buildPolygonsFromGeometry(geometry);
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +var getSharedVerticesInOrder = function (a, b) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      var v0Index = points.indexOf( v0 ),
 | 
	
		
			
				|  |  | -      v1Index = points.indexOf( v1 ),
 | 
	
		
			
				|  |  | -      v2Index = points.indexOf( v2 ),
 | 
	
		
			
				|  |  | -      v3Index = points.indexOf( v3 );
 | 
	
		
			
				|  |  | +	var aList = a.vertexIds;
 | 
	
		
			
				|  |  | +	var bList = b.vertexIds;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	var sharedVertices = [];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    //  We now have a tetrahedron as the base geometry.
 | 
	
		
			
				|  |  | -    //  Now we must subdivide the
 | 
	
		
			
				|  |  | +	aList.forEach((vId) => {
 | 
	
		
			
				|  |  | +		if (bList.includes(vId)) {
 | 
	
		
			
				|  |  | +			sharedVertices.push(vId);
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	});
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      var tetrahedron =[
 | 
	
		
			
				|  |  | -        [ v2Index, v1Index, v0Index ],
 | 
	
		
			
				|  |  | -        [ v1Index, v3Index, v0Index ],
 | 
	
		
			
				|  |  | -        [ v2Index, v3Index, v1Index ],
 | 
	
		
			
				|  |  | -        [ v0Index, v3Index, v2Index ],
 | 
	
		
			
				|  |  | -    ];
 | 
	
		
			
				|  |  | +	if (sharedVertices.length < 2) return [];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	// console.log("TRYING aList:", aList, ", bList:", bList, ", sharedVertices:", sharedVertices);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	if (sharedVertices.includes(aList[0]) && sharedVertices.includes(aList[aList.length - 1])) {
 | 
	
		
			
				|  |  | +		// Vertices on both edges are bad, so shift them once to the left
 | 
	
		
			
				|  |  | +		aList.push(aList.shift());
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    subaA.subVectors( v1, v0 ).normalize();
 | 
	
		
			
				|  |  | -    subaB.subVectors( v2, v0 ).normalize();
 | 
	
		
			
				|  |  | -    subC.subVectors ( v3, v0 ).normalize();
 | 
	
		
			
				|  |  | -    var sign  = subC.dot( new THREE.Vector3().crossVectors( subaB, subaA ));
 | 
	
		
			
				|  |  | +	if (sharedVertices.includes(bList[0]) && sharedVertices.includes(bList[bList.length - 1])) {
 | 
	
		
			
				|  |  | +		// Vertices on both edges are bad, so shift them once to the left
 | 
	
		
			
				|  |  | +		bList.push(bList.shift());
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	// Again!
 | 
	
		
			
				|  |  | +	sharedVertices = [];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // Reverse the winding if negative sign
 | 
	
		
			
				|  |  | -    if( sign < 0 ){
 | 
	
		
			
				|  |  | -      tetrahedron[0].reverse();
 | 
	
		
			
				|  |  | -      tetrahedron[1].reverse();
 | 
	
		
			
				|  |  | -      tetrahedron[2].reverse();
 | 
	
		
			
				|  |  | -      tetrahedron[3].reverse();
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +	aList.forEach((vId) => {
 | 
	
		
			
				|  |  | +		if (bList.includes(vId)) {
 | 
	
		
			
				|  |  | +			sharedVertices.push(vId);
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	});
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	return sharedVertices;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    //One for each face of the pyramid
 | 
	
		
			
				|  |  | -    var pointsCloned = points.slice();
 | 
	
		
			
				|  |  | -    pointsCloned.splice( pointsCloned.indexOf( v0 ), 1 );
 | 
	
		
			
				|  |  | -    pointsCloned.splice( pointsCloned.indexOf( v1 ), 1 );
 | 
	
		
			
				|  |  | -    pointsCloned.splice( pointsCloned.indexOf( v2 ), 1 );
 | 
	
		
			
				|  |  | -    pointsCloned.splice( pointsCloned.indexOf( v3 ), 1 );
 | 
	
		
			
				|  |  | +var groupNavMesh = function (navigationMesh) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	var saveObj = {};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    var i = tetrahedron.length;
 | 
	
		
			
				|  |  | -    while( i-- > 0 ){
 | 
	
		
			
				|  |  | -      assignPoints( tetrahedron[i], pointsCloned, points );
 | 
	
		
			
				|  |  | -      if( tetrahedron[i].visiblePoints !== undefined ){
 | 
	
		
			
				|  |  | -        faceStack.push( tetrahedron[i] );
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -      faces.push( tetrahedron[i] );
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +	navigationMesh.vertices.forEach((v) => {
 | 
	
		
			
				|  |  | +		v.x = utils.roundNumber(v.x, 2);
 | 
	
		
			
				|  |  | +		v.y = utils.roundNumber(v.y, 2);
 | 
	
		
			
				|  |  | +		v.z = utils.roundNumber(v.z, 2);
 | 
	
		
			
				|  |  | +	});
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    process( points );
 | 
	
		
			
				|  |  | +	saveObj.vertices = navigationMesh.vertices;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	var groups = buildPolygonGroups(navigationMesh);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    //  Assign to our geometry object
 | 
	
		
			
				|  |  | +	saveObj.groups = [];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    var ll = faces.length;
 | 
	
		
			
				|  |  | -    while( ll-- > 0 ){
 | 
	
		
			
				|  |  | -      geometry.faces[ll] = new THREE.Face3( faces[ll][2], faces[ll][1], faces[ll][0], faces[ll].normal )
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +	var findPolygonIndex = function (group, p) {
 | 
	
		
			
				|  |  | +		for (var i = 0; i < group.length; i++) {
 | 
	
		
			
				|  |  | +			if (p === group[i]) return i;
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    geometry.normalsNeedUpdate = true;
 | 
	
		
			
				|  |  | +	groups.forEach((group) => {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    return geometry;
 | 
	
		
			
				|  |  | +		var newGroup = [];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +		group.forEach((p) => {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -}())
 | 
	
		
			
				|  |  | +			var neighbours = [];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{}],81:[function(require,module,exports){
 | 
	
		
			
				|  |  | -var EPS = 0.1;
 | 
	
		
			
				|  |  | +			p.neighbours.forEach((n) => {
 | 
	
		
			
				|  |  | +				neighbours.push(findPolygonIndex(group, n));
 | 
	
		
			
				|  |  | +			});
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -module.exports = {
 | 
	
		
			
				|  |  | -  schema: {
 | 
	
		
			
				|  |  | -    enabled: {default: true},
 | 
	
		
			
				|  |  | -    mode: {default: 'teleport', oneOf: ['teleport', 'animate']},
 | 
	
		
			
				|  |  | -    animateSpeed: {default: 3.0}
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  init: function () {
 | 
	
		
			
				|  |  | -    this.active = true;
 | 
	
		
			
				|  |  | -    this.checkpoint = null;
 | 
	
		
			
				|  |  | +			// Build a portal list to each neighbour
 | 
	
		
			
				|  |  | +			var portals = [];
 | 
	
		
			
				|  |  | +			p.neighbours.forEach((n) => {
 | 
	
		
			
				|  |  | +				portals.push(getSharedVerticesInOrder(p, n));
 | 
	
		
			
				|  |  | +			});
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    this.offset = new THREE.Vector3();
 | 
	
		
			
				|  |  | -    this.position = new THREE.Vector3();
 | 
	
		
			
				|  |  | -    this.targetPosition = new THREE.Vector3();
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  play: function () { this.active = true; },
 | 
	
		
			
				|  |  | -  pause: function () { this.active = false; },
 | 
	
		
			
				|  |  | +			p.centroid.x = utils.roundNumber(p.centroid.x, 2);
 | 
	
		
			
				|  |  | +			p.centroid.y = utils.roundNumber(p.centroid.y, 2);
 | 
	
		
			
				|  |  | +			p.centroid.z = utils.roundNumber(p.centroid.z, 2);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  setCheckpoint: function (checkpoint) {
 | 
	
		
			
				|  |  | -    var el = this.el;
 | 
	
		
			
				|  |  | +			newGroup.push({
 | 
	
		
			
				|  |  | +				id: findPolygonIndex(group, p),
 | 
	
		
			
				|  |  | +				neighbours: neighbours,
 | 
	
		
			
				|  |  | +				vertexIds: p.vertexIds,
 | 
	
		
			
				|  |  | +				centroid: p.centroid,
 | 
	
		
			
				|  |  | +				portals: portals
 | 
	
		
			
				|  |  | +			});
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (!this.active) return;
 | 
	
		
			
				|  |  | -    if (this.checkpoint === checkpoint) return;
 | 
	
		
			
				|  |  | +		});
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (this.checkpoint) {
 | 
	
		
			
				|  |  | -      el.emit('navigation-end', {checkpoint: checkpoint});
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +		saveObj.groups.push(newGroup);
 | 
	
		
			
				|  |  | +	});
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    this.checkpoint = checkpoint;
 | 
	
		
			
				|  |  | -    el.emit('navigation-start', {checkpoint: checkpoint});
 | 
	
		
			
				|  |  | +	return saveObj;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (this.data.mode === 'teleport') {
 | 
	
		
			
				|  |  | -      this.sync();
 | 
	
		
			
				|  |  | -      this.el.setAttribute('position', this.targetPosition);
 | 
	
		
			
				|  |  | -      this.checkpoint = null;
 | 
	
		
			
				|  |  | -      el.emit('navigation-end', {checkpoint: checkpoint});
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +var zoneNodes = {};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  isVelocityActive: function () {
 | 
	
		
			
				|  |  | -    return !!(this.active && this.checkpoint);
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +module.exports = {
 | 
	
		
			
				|  |  | +	buildNodes: function (geometry) {
 | 
	
		
			
				|  |  | +		var navigationMesh = buildNavigationMesh(geometry);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  getVelocity: function () {
 | 
	
		
			
				|  |  | -    if (!this.active) return;
 | 
	
		
			
				|  |  | +		var zoneNodes = groupNavMesh(navigationMesh);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    var data = this.data,
 | 
	
		
			
				|  |  | -        offset = this.offset,
 | 
	
		
			
				|  |  | -        position = this.position,
 | 
	
		
			
				|  |  | -        targetPosition = this.targetPosition,
 | 
	
		
			
				|  |  | -        checkpoint = this.checkpoint;
 | 
	
		
			
				|  |  | +		return zoneNodes;
 | 
	
		
			
				|  |  | +	},
 | 
	
		
			
				|  |  | +	setZoneData: function (zone, data) {
 | 
	
		
			
				|  |  | +		zoneNodes[zone] = data;
 | 
	
		
			
				|  |  | +	},
 | 
	
		
			
				|  |  | +	getGroup: function (zone, position) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    this.sync();
 | 
	
		
			
				|  |  | -    if (position.distanceTo(targetPosition) < EPS) {
 | 
	
		
			
				|  |  | -      this.checkpoint = null;
 | 
	
		
			
				|  |  | -      this.el.emit('navigation-end', {checkpoint: checkpoint});
 | 
	
		
			
				|  |  | -      return offset.set(0, 0, 0);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    offset.setLength(data.animateSpeed);
 | 
	
		
			
				|  |  | -    return offset;
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +		if (!zoneNodes[zone]) return null;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  sync: function () {
 | 
	
		
			
				|  |  | -    var offset = this.offset,
 | 
	
		
			
				|  |  | -        position = this.position,
 | 
	
		
			
				|  |  | -        targetPosition = this.targetPosition;
 | 
	
		
			
				|  |  | +		var closestNodeGroup = null;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    position.copy(this.el.getAttribute('position'));
 | 
	
		
			
				|  |  | -    targetPosition.copy(this.checkpoint.object3D.getWorldPosition());
 | 
	
		
			
				|  |  | -    targetPosition.add(this.checkpoint.components.checkpoint.getOffset());
 | 
	
		
			
				|  |  | -    offset.copy(targetPosition).sub(position);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | +		var distance = Math.pow(50, 2);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{}],82:[function(require,module,exports){
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * Gamepad controls for A-Frame.
 | 
	
		
			
				|  |  | - *
 | 
	
		
			
				|  |  | - * Stripped-down version of: https://github.com/donmccurdy/aframe-gamepad-controls
 | 
	
		
			
				|  |  | - *
 | 
	
		
			
				|  |  | - * For more information about the Gamepad API, see:
 | 
	
		
			
				|  |  | - * https://developer.mozilla.org/en-US/docs/Web/API/Gamepad_API/Using_the_Gamepad_API
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | +		zoneNodes[zone].groups.forEach((group, index) => {
 | 
	
		
			
				|  |  | +			group.forEach((node) => {
 | 
	
		
			
				|  |  | +				var measuredDistance = utils.distanceToSquared(node.centroid, position);
 | 
	
		
			
				|  |  | +				if (measuredDistance < distance) {
 | 
	
		
			
				|  |  | +					closestNodeGroup = index;
 | 
	
		
			
				|  |  | +					distance = measuredDistance;
 | 
	
		
			
				|  |  | +				}
 | 
	
		
			
				|  |  | +			});
 | 
	
		
			
				|  |  | +		});
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -var GamepadButton = require('../../lib/GamepadButton'),
 | 
	
		
			
				|  |  | -    GamepadButtonEvent = require('../../lib/GamepadButtonEvent');
 | 
	
		
			
				|  |  | +		return closestNodeGroup;
 | 
	
		
			
				|  |  | +	},
 | 
	
		
			
				|  |  | +	getRandomNode: function (zone, group, nearPosition, nearRange) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -var JOYSTICK_EPS = 0.2;
 | 
	
		
			
				|  |  | +		if (!zoneNodes[zone]) return new THREE.Vector3();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -module.exports = {
 | 
	
		
			
				|  |  | +		nearPosition = nearPosition || null;
 | 
	
		
			
				|  |  | +		nearRange = nearRange || 0;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /*******************************************************************
 | 
	
		
			
				|  |  | -   * Statics
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | +		var candidates = [];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  GamepadButton: GamepadButton,
 | 
	
		
			
				|  |  | +		var polygons = zoneNodes[zone].groups[group];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /*******************************************************************
 | 
	
		
			
				|  |  | -   * Schema
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | +		polygons.forEach((p) => {
 | 
	
		
			
				|  |  | +			if (nearPosition && nearRange) {
 | 
	
		
			
				|  |  | +				if (utils.distanceToSquared(nearPosition, p.centroid) < nearRange * nearRange) {
 | 
	
		
			
				|  |  | +					candidates.push(p.centroid);
 | 
	
		
			
				|  |  | +				}
 | 
	
		
			
				|  |  | +			} else {
 | 
	
		
			
				|  |  | +				candidates.push(p.centroid);
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +		});
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  schema: {
 | 
	
		
			
				|  |  | -    // Controller 0-3
 | 
	
		
			
				|  |  | -    controller:        { default: 0, oneOf: [0, 1, 2, 3] },
 | 
	
		
			
				|  |  | +		return utils.sample(candidates) || new THREE.Vector3();
 | 
	
		
			
				|  |  | +	},
 | 
	
		
			
				|  |  | +	getClosestNode: function (position, zone, group, checkPolygon = false) {
 | 
	
		
			
				|  |  | +		const nodes = zoneNodes[zone].groups[group];
 | 
	
		
			
				|  |  | +		const vertices = zoneNodes[zone].vertices;
 | 
	
		
			
				|  |  | +		let closestNode = null;
 | 
	
		
			
				|  |  | +		let closestDistance = Infinity;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // Enable/disable features
 | 
	
		
			
				|  |  | -    enabled:           { default: true },
 | 
	
		
			
				|  |  | +		nodes.forEach((node) => {
 | 
	
		
			
				|  |  | +			const distance = utils.distanceToSquared(node.centroid, position);
 | 
	
		
			
				|  |  | +			if (distance < closestDistance
 | 
	
		
			
				|  |  | +					&& (!checkPolygon || utils.isVectorInPolygon(position, node, vertices))) {
 | 
	
		
			
				|  |  | +				closestNode = node;
 | 
	
		
			
				|  |  | +				closestDistance = distance;
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +		});
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // Debugging
 | 
	
		
			
				|  |  | -    debug:             { default: false }
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +		return closestNode;
 | 
	
		
			
				|  |  | +	},
 | 
	
		
			
				|  |  | +	findPath: function (startPosition, targetPosition, zone, group) {
 | 
	
		
			
				|  |  | +		const nodes = zoneNodes[zone].groups[group];
 | 
	
		
			
				|  |  | +		const vertices = zoneNodes[zone].vertices;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /*******************************************************************
 | 
	
		
			
				|  |  | -   * Core
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | +		const closestNode = this.getClosestNode(startPosition, zone, group);
 | 
	
		
			
				|  |  | +		const farthestNode = this.getClosestNode(targetPosition, zone, group, true);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /**
 | 
	
		
			
				|  |  | -   * Called once when component is attached. Generally for initial setup.
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | -  init: function () {
 | 
	
		
			
				|  |  | -    var scene = this.el.sceneEl;
 | 
	
		
			
				|  |  | -    this.prevTime = window.performance.now();
 | 
	
		
			
				|  |  | +		// If we can't find any node, just go straight to the target
 | 
	
		
			
				|  |  | +		if (!closestNode || !farthestNode) {
 | 
	
		
			
				|  |  | +			return null;
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // Button state
 | 
	
		
			
				|  |  | -    this.buttons = {};
 | 
	
		
			
				|  |  | +		const paths = AStar.search(nodes, closestNode, farthestNode);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    scene.addBehavior(this);
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +		const getPortalFromTo = function (a, b) {
 | 
	
		
			
				|  |  | +			for (var i = 0; i < a.neighbours.length; i++) {
 | 
	
		
			
				|  |  | +				if (a.neighbours[i] === b.id) {
 | 
	
		
			
				|  |  | +					return a.portals[i];
 | 
	
		
			
				|  |  | +				}
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +		};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /**
 | 
	
		
			
				|  |  | -   * Called when component is attached and when component data changes.
 | 
	
		
			
				|  |  | -   * Generally modifies the entity based on the data.
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | -  update: function () { this.tick(); },
 | 
	
		
			
				|  |  | +		// We have the corridor, now pull the rope.
 | 
	
		
			
				|  |  | +		const channel = new Channel();
 | 
	
		
			
				|  |  | +		channel.push(startPosition);
 | 
	
		
			
				|  |  | +		for (let i = 0; i < paths.length; i++) {
 | 
	
		
			
				|  |  | +			const polygon = paths[i];
 | 
	
		
			
				|  |  | +			const nextPolygon = paths[i + 1];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /**
 | 
	
		
			
				|  |  | -   * Called on each iteration of main render loop.
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | -  tick: function () {
 | 
	
		
			
				|  |  | -    this.updateButtonState();
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +			if (nextPolygon) {
 | 
	
		
			
				|  |  | +				const portals = getPortalFromTo(polygon, nextPolygon);
 | 
	
		
			
				|  |  | +				channel.push(
 | 
	
		
			
				|  |  | +					vertices[portals[0]],
 | 
	
		
			
				|  |  | +					vertices[portals[1]]
 | 
	
		
			
				|  |  | +				);
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +		channel.push(targetPosition);
 | 
	
		
			
				|  |  | +		channel.stringPull();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /**
 | 
	
		
			
				|  |  | -   * Called when a component is removed (e.g., via removeAttribute).
 | 
	
		
			
				|  |  | -   * Generally undoes all modifications to the entity.
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | -  remove: function () { },
 | 
	
		
			
				|  |  | +		// Return the path, omitting first position (which is already known).
 | 
	
		
			
				|  |  | +		const path = channel.path.map((c) => new THREE.Vector3(c.x, c.y, c.z));
 | 
	
		
			
				|  |  | +		path.shift();
 | 
	
		
			
				|  |  | +		return path;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /*******************************************************************
 | 
	
		
			
				|  |  | -   * Universal controls - movement
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | +},{"./AStar":79,"./Channel":81,"./utils":83}],83:[function(require,module,exports){
 | 
	
		
			
				|  |  | +class Utils {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  isVelocityActive: function () {
 | 
	
		
			
				|  |  | -    if (!this.data.enabled || !this.isConnected()) return false;
 | 
	
		
			
				|  |  | +  static computeCentroids (geometry) {
 | 
	
		
			
				|  |  | +    var f, fl, face;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    var dpad = this.getDpad(),
 | 
	
		
			
				|  |  | -        joystick0 = this.getJoystick(0),
 | 
	
		
			
				|  |  | -        inputX = dpad.x || joystick0.x,
 | 
	
		
			
				|  |  | -        inputY = dpad.y || joystick0.y;
 | 
	
		
			
				|  |  | +    for ( f = 0, fl = geometry.faces.length; f < fl; f ++ ) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    return Math.abs(inputX) > JOYSTICK_EPS || Math.abs(inputY) > JOYSTICK_EPS;
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +      face = geometry.faces[ f ];
 | 
	
		
			
				|  |  | +      face.centroid = new THREE.Vector3( 0, 0, 0 );
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  getVelocityDelta: function () {
 | 
	
		
			
				|  |  | -    var dpad = this.getDpad(),
 | 
	
		
			
				|  |  | -        joystick0 = this.getJoystick(0),
 | 
	
		
			
				|  |  | -        inputX = dpad.x || joystick0.x,
 | 
	
		
			
				|  |  | -        inputY = dpad.y || joystick0.y,
 | 
	
		
			
				|  |  | -        dVelocity = new THREE.Vector3();
 | 
	
		
			
				|  |  | +      face.centroid.add( geometry.vertices[ face.a ] );
 | 
	
		
			
				|  |  | +      face.centroid.add( geometry.vertices[ face.b ] );
 | 
	
		
			
				|  |  | +      face.centroid.add( geometry.vertices[ face.c ] );
 | 
	
		
			
				|  |  | +      face.centroid.divideScalar( 3 );
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (Math.abs(inputX) > JOYSTICK_EPS) {
 | 
	
		
			
				|  |  | -      dVelocity.x += inputX;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    if (Math.abs(inputY) > JOYSTICK_EPS) {
 | 
	
		
			
				|  |  | -      dVelocity.z += inputY;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    return dVelocity;
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +  static roundNumber (number, decimals) {
 | 
	
		
			
				|  |  | +    var newnumber = Number(number + '').toFixed(parseInt(decimals));
 | 
	
		
			
				|  |  | +    return parseFloat(newnumber);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /*******************************************************************
 | 
	
		
			
				|  |  | -   * Universal controls - rotation
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | +  static sample (list) {
 | 
	
		
			
				|  |  | +    return list[Math.floor(Math.random() * list.length)];
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  isRotationActive: function () {
 | 
	
		
			
				|  |  | -    if (!this.data.enabled || !this.isConnected()) return false;
 | 
	
		
			
				|  |  | +  static mergeVertexIds (aList, bList) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    var joystick1 = this.getJoystick(1);
 | 
	
		
			
				|  |  | +    var sharedVertices = [];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    return Math.abs(joystick1.x) > JOYSTICK_EPS || Math.abs(joystick1.y) > JOYSTICK_EPS;
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +    aList.forEach((vID) => {
 | 
	
		
			
				|  |  | +      if (bList.indexOf(vID) >= 0) {
 | 
	
		
			
				|  |  | +        sharedVertices.push(vID);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  getRotationDelta: function () {
 | 
	
		
			
				|  |  | -    var lookVector = this.getJoystick(1);
 | 
	
		
			
				|  |  | -    if (Math.abs(lookVector.x) <= JOYSTICK_EPS) lookVector.x = 0;
 | 
	
		
			
				|  |  | -    if (Math.abs(lookVector.y) <= JOYSTICK_EPS) lookVector.y = 0;
 | 
	
		
			
				|  |  | -    return lookVector;
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +    if (sharedVertices.length < 2) return [];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /*******************************************************************
 | 
	
		
			
				|  |  | -   * Button events
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | +    if (sharedVertices.includes(aList[0]) && sharedVertices.includes(aList[aList.length - 1])) {
 | 
	
		
			
				|  |  | +      // Vertices on both edges are bad, so shift them once to the left
 | 
	
		
			
				|  |  | +      aList.push(aList.shift());
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  updateButtonState: function () {
 | 
	
		
			
				|  |  | -    var gamepad = this.getGamepad();
 | 
	
		
			
				|  |  | -    if (this.data.enabled && gamepad) {
 | 
	
		
			
				|  |  | +    if (sharedVertices.includes(bList[0]) && sharedVertices.includes(bList[bList.length - 1])) {
 | 
	
		
			
				|  |  | +      // Vertices on both edges are bad, so shift them once to the left
 | 
	
		
			
				|  |  | +      bList.push(bList.shift());
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      // Fire DOM events for button state changes.
 | 
	
		
			
				|  |  | -      for (var i = 0; i < gamepad.buttons.length; i++) {
 | 
	
		
			
				|  |  | -        if (gamepad.buttons[i].pressed && !this.buttons[i]) {
 | 
	
		
			
				|  |  | -          this.emit(new GamepadButtonEvent('gamepadbuttondown', i, gamepad.buttons[i]));
 | 
	
		
			
				|  |  | -        } else if (!gamepad.buttons[i].pressed && this.buttons[i]) {
 | 
	
		
			
				|  |  | -          this.emit(new GamepadButtonEvent('gamepadbuttonup', i, gamepad.buttons[i]));
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        this.buttons[i] = gamepad.buttons[i].pressed;
 | 
	
		
			
				|  |  | +    // Again!
 | 
	
		
			
				|  |  | +    sharedVertices = [];
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    aList.forEach((vId) => {
 | 
	
		
			
				|  |  | +      if (bList.includes(vId)) {
 | 
	
		
			
				|  |  | +        sharedVertices.push(vId);
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    } else if (Object.keys(this.buttons)) {
 | 
	
		
			
				|  |  | -      // Reset state if controls are disabled or controller is lost.
 | 
	
		
			
				|  |  | -      this.buttons = {};
 | 
	
		
			
				|  |  | +    var clockwiseMostSharedVertex = sharedVertices[1];
 | 
	
		
			
				|  |  | +    var counterClockwiseMostSharedVertex = sharedVertices[0];
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    var cList = aList.slice();
 | 
	
		
			
				|  |  | +    while (cList[0] !== clockwiseMostSharedVertex) {
 | 
	
		
			
				|  |  | +      cList.push(cList.shift());
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  emit: function (event) {
 | 
	
		
			
				|  |  | -    // Emit original event.
 | 
	
		
			
				|  |  | -    this.el.emit(event.type, event);
 | 
	
		
			
				|  |  | +    var c = 0;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // Emit convenience event, identifying button index.
 | 
	
		
			
				|  |  | -    this.el.emit(
 | 
	
		
			
				|  |  | -      event.type + ':' + event.index,
 | 
	
		
			
				|  |  | -      new GamepadButtonEvent(event.type, event.index, event)
 | 
	
		
			
				|  |  | -    );
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +    var temp = bList.slice();
 | 
	
		
			
				|  |  | +    while (temp[0] !== counterClockwiseMostSharedVertex) {
 | 
	
		
			
				|  |  | +      temp.push(temp.shift());
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /*******************************************************************
 | 
	
		
			
				|  |  | -   * Gamepad state
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | +      if (c++ > 10) throw new Error('Unexpected state');
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /**
 | 
	
		
			
				|  |  | -   * Returns the Gamepad instance attached to the component. If connected,
 | 
	
		
			
				|  |  | -   * a proxy-controls component may provide access to Gamepad input from a
 | 
	
		
			
				|  |  | -   * remote device.
 | 
	
		
			
				|  |  | -   *
 | 
	
		
			
				|  |  | -   * @return {Gamepad}
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | -  getGamepad: function () {
 | 
	
		
			
				|  |  | -    var localGamepad = navigator.getGamepads
 | 
	
		
			
				|  |  | -          && navigator.getGamepads()[this.data.controller],
 | 
	
		
			
				|  |  | -        proxyControls = this.el.sceneEl.components['proxy-controls'],
 | 
	
		
			
				|  |  | -        proxyGamepad = proxyControls && proxyControls.isConnected()
 | 
	
		
			
				|  |  | -          && proxyControls.getGamepad(this.data.controller);
 | 
	
		
			
				|  |  | -    return proxyGamepad || localGamepad;
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +    // Shave
 | 
	
		
			
				|  |  | +    temp.shift();
 | 
	
		
			
				|  |  | +    temp.pop();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /**
 | 
	
		
			
				|  |  | -   * Returns the state of the given button.
 | 
	
		
			
				|  |  | -   * @param  {number} index The button (0-N) for which to find state.
 | 
	
		
			
				|  |  | -   * @return {GamepadButton}
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | -  getButton: function (index) {
 | 
	
		
			
				|  |  | -    return this.getGamepad().buttons[index];
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +    cList = cList.concat(temp);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /**
 | 
	
		
			
				|  |  | -   * Returns state of the given axis. Axes are labelled 0-N, where 0-1 will
 | 
	
		
			
				|  |  | -   * represent X/Y on the first joystick, and 2-3 X/Y on the second.
 | 
	
		
			
				|  |  | -   * @param  {number} index The axis (0-N) for which to find state.
 | 
	
		
			
				|  |  | -   * @return {number} On the interval [-1,1].
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | -  getAxis: function (index) {
 | 
	
		
			
				|  |  | -    return this.getGamepad().axes[index];
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +    return cList;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /**
 | 
	
		
			
				|  |  | -   * Returns the state of the given joystick (0 or 1) as a THREE.Vector2.
 | 
	
		
			
				|  |  | -   * @param  {number} id The joystick (0, 1) for which to find state.
 | 
	
		
			
				|  |  | -   * @return {THREE.Vector2}
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | -  getJoystick: function (index) {
 | 
	
		
			
				|  |  | -    var gamepad = this.getGamepad();
 | 
	
		
			
				|  |  | -    switch (index) {
 | 
	
		
			
				|  |  | -      case 0: return new THREE.Vector2(gamepad.axes[0], gamepad.axes[1]);
 | 
	
		
			
				|  |  | -      case 1: return new THREE.Vector2(gamepad.axes[2], gamepad.axes[3]);
 | 
	
		
			
				|  |  | -      default: throw new Error('Unexpected joystick index "%d".', index);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +  static setPolygonCentroid (polygon, navigationMesh) {
 | 
	
		
			
				|  |  | +    var sum = new THREE.Vector3();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /**
 | 
	
		
			
				|  |  | -   * Returns the state of the dpad as a THREE.Vector2.
 | 
	
		
			
				|  |  | -   * @return {THREE.Vector2}
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | -  getDpad: function () {
 | 
	
		
			
				|  |  | -    var gamepad = this.getGamepad();
 | 
	
		
			
				|  |  | -    if (!gamepad.buttons[GamepadButton.DPAD_RIGHT]) {
 | 
	
		
			
				|  |  | -      return new THREE.Vector2();
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    return new THREE.Vector2(
 | 
	
		
			
				|  |  | -      (gamepad.buttons[GamepadButton.DPAD_RIGHT].pressed ? 1 : 0)
 | 
	
		
			
				|  |  | -      + (gamepad.buttons[GamepadButton.DPAD_LEFT].pressed ? -1 : 0),
 | 
	
		
			
				|  |  | -      (gamepad.buttons[GamepadButton.DPAD_UP].pressed ? -1 : 0)
 | 
	
		
			
				|  |  | -      + (gamepad.buttons[GamepadButton.DPAD_DOWN].pressed ? 1 : 0)
 | 
	
		
			
				|  |  | -    );
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +    var vertices = navigationMesh.vertices;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /**
 | 
	
		
			
				|  |  | -   * Returns true if the gamepad is currently connected to the system.
 | 
	
		
			
				|  |  | -   * @return {boolean}
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | -  isConnected: function () {
 | 
	
		
			
				|  |  | -    var gamepad = this.getGamepad();
 | 
	
		
			
				|  |  | -    return !!(gamepad && gamepad.connected);
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +    polygon.vertexIds.forEach((vId) => {
 | 
	
		
			
				|  |  | +      sum.add(vertices[vId]);
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /**
 | 
	
		
			
				|  |  | -   * Returns a string containing some information about the controller. Result
 | 
	
		
			
				|  |  | -   * may vary across browsers, for a given controller.
 | 
	
		
			
				|  |  | -   * @return {string}
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | -  getID: function () {
 | 
	
		
			
				|  |  | -    return this.getGamepad().id;
 | 
	
		
			
				|  |  | +    sum.divideScalar(polygon.vertexIds.length);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    polygon.centroid.copy(sum);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{"../../lib/GamepadButton":4,"../../lib/GamepadButtonEvent":5}],83:[function(require,module,exports){
 | 
	
		
			
				|  |  | -var radToDeg = THREE.Math.radToDeg,
 | 
	
		
			
				|  |  | -    isMobile = AFRAME.utils.device.isMobile();
 | 
	
		
			
				|  |  | +  static cleanPolygon (polygon, navigationMesh) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -module.exports = {
 | 
	
		
			
				|  |  | -  schema: {
 | 
	
		
			
				|  |  | -    enabled: {default: true},
 | 
	
		
			
				|  |  | -    standing: {default: true}
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +    var newVertexIds = [];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  init: function () {
 | 
	
		
			
				|  |  | -    this.isPositionCalibrated = false;
 | 
	
		
			
				|  |  | -    this.dolly = new THREE.Object3D();
 | 
	
		
			
				|  |  | -    this.hmdEuler = new THREE.Euler();
 | 
	
		
			
				|  |  | -    this.previousHMDPosition = new THREE.Vector3();
 | 
	
		
			
				|  |  | -    this.deltaHMDPosition = new THREE.Vector3();
 | 
	
		
			
				|  |  | -    this.vrControls = new THREE.VRControls(this.dolly);
 | 
	
		
			
				|  |  | -    this.rotation = new THREE.Vector3();
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +    var vertices = navigationMesh.vertices;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  update: function () {
 | 
	
		
			
				|  |  | -    var data = this.data;
 | 
	
		
			
				|  |  | -    var vrControls = this.vrControls;
 | 
	
		
			
				|  |  | -    vrControls.standing = data.standing;
 | 
	
		
			
				|  |  | -    vrControls.update();
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +    for (var i = 0; i < polygon.vertexIds.length; i++) {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      var vertex = vertices[polygon.vertexIds[i]];
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      var nextVertexId, previousVertexId;
 | 
	
		
			
				|  |  | +      var nextVertex, previousVertex;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      // console.log("nextVertex: ", nextVertex);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      if (i === 0) {
 | 
	
		
			
				|  |  | +        nextVertexId = polygon.vertexIds[1];
 | 
	
		
			
				|  |  | +        previousVertexId = polygon.vertexIds[polygon.vertexIds.length - 1];
 | 
	
		
			
				|  |  | +      } else if (i === polygon.vertexIds.length - 1) {
 | 
	
		
			
				|  |  | +        nextVertexId = polygon.vertexIds[0];
 | 
	
		
			
				|  |  | +        previousVertexId = polygon.vertexIds[polygon.vertexIds.length - 2];
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        nextVertexId = polygon.vertexIds[i + 1];
 | 
	
		
			
				|  |  | +        previousVertexId = polygon.vertexIds[i - 1];
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  tick: function () {
 | 
	
		
			
				|  |  | -    this.vrControls.update();
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +      nextVertex = vertices[nextVertexId];
 | 
	
		
			
				|  |  | +      previousVertex = vertices[previousVertexId];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  remove: function () {
 | 
	
		
			
				|  |  | -    this.vrControls.dispose();
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +      var a = nextVertex.clone().sub(vertex);
 | 
	
		
			
				|  |  | +      var b = previousVertex.clone().sub(vertex);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  isRotationActive: function () {
 | 
	
		
			
				|  |  | -    var hmdEuler = this.hmdEuler;
 | 
	
		
			
				|  |  | -    if (!this.data.enabled || !(this.el.sceneEl.is('vr-mode') || isMobile)) {
 | 
	
		
			
				|  |  | -      return false;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    hmdEuler.setFromQuaternion(this.dolly.quaternion, 'YXZ');
 | 
	
		
			
				|  |  | -    return !isNullVector(hmdEuler);
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +      var angle = a.angleTo(b);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  getRotation: function () {
 | 
	
		
			
				|  |  | -    var hmdEuler = this.hmdEuler;
 | 
	
		
			
				|  |  | -    return this.rotation.set(
 | 
	
		
			
				|  |  | -      radToDeg(hmdEuler.x),
 | 
	
		
			
				|  |  | -      radToDeg(hmdEuler.y),
 | 
	
		
			
				|  |  | -      radToDeg(hmdEuler.z)
 | 
	
		
			
				|  |  | -    );
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +      // console.log(angle);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  isVelocityActive: function () {
 | 
	
		
			
				|  |  | -    var deltaHMDPosition = this.deltaHMDPosition;
 | 
	
		
			
				|  |  | -    var previousHMDPosition = this.previousHMDPosition;
 | 
	
		
			
				|  |  | -    var currentHMDPosition = this.calculateHMDPosition();
 | 
	
		
			
				|  |  | -    this.isPositionCalibrated = this.isPositionCalibrated || !isNullVector(previousHMDPosition);
 | 
	
		
			
				|  |  | -    if (!this.data.enabled || !this.el.sceneEl.is('vr-mode') || isMobile) {
 | 
	
		
			
				|  |  | -      return false;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    deltaHMDPosition.copy(currentHMDPosition).sub(previousHMDPosition);
 | 
	
		
			
				|  |  | -    previousHMDPosition.copy(currentHMDPosition);
 | 
	
		
			
				|  |  | -    return this.isPositionCalibrated && !isNullVector(deltaHMDPosition);
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +      if (angle > Math.PI - 0.01 && angle < Math.PI + 0.01) {
 | 
	
		
			
				|  |  | +        // Unneccesary vertex
 | 
	
		
			
				|  |  | +        // console.log("Unneccesary vertex: ", polygon.vertexIds[i]);
 | 
	
		
			
				|  |  | +        // console.log("Angle between "+previousVertexId+", "+polygon.vertexIds[i]+" "+nextVertexId+" was: ", angle);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  getPositionDelta: function () {
 | 
	
		
			
				|  |  | -    return this.deltaHMDPosition;
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  calculateHMDPosition: function () {
 | 
	
		
			
				|  |  | -    var dolly = this.dolly;
 | 
	
		
			
				|  |  | -    var position = new THREE.Vector3();
 | 
	
		
			
				|  |  | -    dolly.updateMatrix();
 | 
	
		
			
				|  |  | -    position.setFromMatrixPosition(dolly.matrix);
 | 
	
		
			
				|  |  | -    return position;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | +        // Remove the neighbours who had this vertex
 | 
	
		
			
				|  |  | +        var goodNeighbours = [];
 | 
	
		
			
				|  |  | +        polygon.neighbours.forEach((neighbour) => {
 | 
	
		
			
				|  |  | +          if (!neighbour.vertexIds.includes(polygon.vertexIds[i])) {
 | 
	
		
			
				|  |  | +            goodNeighbours.push(neighbour);
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  | +        });
 | 
	
		
			
				|  |  | +        polygon.neighbours = goodNeighbours;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -function isNullVector (vector) {
 | 
	
		
			
				|  |  | -  return vector.x === 0 && vector.y === 0 && vector.z === 0;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{}],84:[function(require,module,exports){
 | 
	
		
			
				|  |  | -var physics = require('aframe-physics-system');
 | 
	
		
			
				|  |  | +        // TODO cleanup the list of vertices and rebuild vertexIds for all polygons
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        newVertexIds.push(polygon.vertexIds[i]);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -module.exports = {
 | 
	
		
			
				|  |  | -  'checkpoint-controls': require('./checkpoint-controls'),
 | 
	
		
			
				|  |  | -  'gamepad-controls':    require('./gamepad-controls'),
 | 
	
		
			
				|  |  | -  'hmd-controls':        require('./hmd-controls'),
 | 
	
		
			
				|  |  | -  'keyboard-controls':   require('./keyboard-controls'),
 | 
	
		
			
				|  |  | -  'mouse-controls':      require('./mouse-controls'),
 | 
	
		
			
				|  |  | -  'touch-controls':      require('./touch-controls'),
 | 
	
		
			
				|  |  | -  'universal-controls':  require('./universal-controls'),
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  registerAll: function (AFRAME) {
 | 
	
		
			
				|  |  | -    if (this._registered) return;
 | 
	
		
			
				|  |  | +    // console.log("New vertexIds: ", newVertexIds);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    AFRAME = AFRAME || window.AFRAME;
 | 
	
		
			
				|  |  | +    polygon.vertexIds = newVertexIds;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    physics.registerAll();
 | 
	
		
			
				|  |  | -    if (!AFRAME.components['checkpoint-controls'])  AFRAME.registerComponent('checkpoint-controls', this['checkpoint-controls']);
 | 
	
		
			
				|  |  | -    if (!AFRAME.components['gamepad-controls'])     AFRAME.registerComponent('gamepad-controls',    this['gamepad-controls']);
 | 
	
		
			
				|  |  | -    if (!AFRAME.components['hmd-controls'])         AFRAME.registerComponent('hmd-controls',        this['hmd-controls']);
 | 
	
		
			
				|  |  | -    if (!AFRAME.components['keyboard-controls'])    AFRAME.registerComponent('keyboard-controls',   this['keyboard-controls']);
 | 
	
		
			
				|  |  | -    if (!AFRAME.components['mouse-controls'])       AFRAME.registerComponent('mouse-controls',      this['mouse-controls']);
 | 
	
		
			
				|  |  | -    if (!AFRAME.components['touch-controls'])       AFRAME.registerComponent('touch-controls',      this['touch-controls']);
 | 
	
		
			
				|  |  | -    if (!AFRAME.components['universal-controls'])   AFRAME.registerComponent('universal-controls',  this['universal-controls']);
 | 
	
		
			
				|  |  | +    setPolygonCentroid(polygon, navigationMesh);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    this._registered = true;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{"./checkpoint-controls":81,"./gamepad-controls":82,"./hmd-controls":83,"./keyboard-controls":85,"./mouse-controls":86,"./touch-controls":87,"./universal-controls":88,"aframe-physics-system":11}],85:[function(require,module,exports){
 | 
	
		
			
				|  |  | -require('../../lib/keyboard.polyfill');
 | 
	
		
			
				|  |  | +  static isConvex (polygon, navigationMesh) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -var MAX_DELTA = 0.2,
 | 
	
		
			
				|  |  | -    PROXY_FLAG = '__keyboard-controls-proxy';
 | 
	
		
			
				|  |  | +    var vertices = navigationMesh.vertices;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -var KeyboardEvent = window.KeyboardEvent;
 | 
	
		
			
				|  |  | +    if (polygon.vertexIds.length < 3) return false;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * Keyboard Controls component.
 | 
	
		
			
				|  |  | - *
 | 
	
		
			
				|  |  | - * Stripped-down version of: https://github.com/donmccurdy/aframe-keyboard-controls
 | 
	
		
			
				|  |  | - *
 | 
	
		
			
				|  |  | - * Bind keyboard events to components, or control your entities with the WASD keys.
 | 
	
		
			
				|  |  | - *
 | 
	
		
			
				|  |  | - * Why use KeyboardEvent.code? "This is set to a string representing the key that was pressed to
 | 
	
		
			
				|  |  | - * generate the KeyboardEvent, without taking the current keyboard layout (e.g., QWERTY vs.
 | 
	
		
			
				|  |  | - * Dvorak), locale (e.g., English vs. French), or any modifier keys into account. This is useful
 | 
	
		
			
				|  |  | - * when you care about which physical key was pressed, rather thanwhich character it corresponds
 | 
	
		
			
				|  |  | - * to. For example, if you’re a writing a game, you might want a certain set of keys to move the
 | 
	
		
			
				|  |  | - * player in different directions, and that mapping should ideally be independent of keyboard
 | 
	
		
			
				|  |  | - * layout. See: https://developers.google.com/web/updates/2016/04/keyboardevent-keys-codes
 | 
	
		
			
				|  |  | - *
 | 
	
		
			
				|  |  | - * @namespace wasd-controls
 | 
	
		
			
				|  |  | - * keys the entity moves and if you release it will stop. Easing simulates friction.
 | 
	
		
			
				|  |  | - * to the entity when pressing the keys.
 | 
	
		
			
				|  |  | - * @param {bool} [enabled=true] - To completely enable or disable the controls
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -module.exports = {
 | 
	
		
			
				|  |  | -  schema: {
 | 
	
		
			
				|  |  | -    enabled:           { default: true },
 | 
	
		
			
				|  |  | -    debug:             { default: false }
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +    var convex = true;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  init: function () {
 | 
	
		
			
				|  |  | -    this.dVelocity = new THREE.Vector3();
 | 
	
		
			
				|  |  | -    this.localKeys = {};
 | 
	
		
			
				|  |  | -    this.listeners = {
 | 
	
		
			
				|  |  | -      keydown: this.onKeyDown.bind(this),
 | 
	
		
			
				|  |  | -      keyup: this.onKeyUp.bind(this),
 | 
	
		
			
				|  |  | -      blur: this.onBlur.bind(this)
 | 
	
		
			
				|  |  | -    };
 | 
	
		
			
				|  |  | -    this.attachEventListeners();
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +    var total = 0;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /*******************************************************************
 | 
	
		
			
				|  |  | -  * Movement
 | 
	
		
			
				|  |  | -  */
 | 
	
		
			
				|  |  | +    var results = [];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  isVelocityActive: function () {
 | 
	
		
			
				|  |  | -    return this.data.enabled && !!Object.keys(this.getKeys()).length;
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +    for (var i = 0; i < polygon.vertexIds.length; i++) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  getVelocityDelta: function () {
 | 
	
		
			
				|  |  | -    var data = this.data,
 | 
	
		
			
				|  |  | -        keys = this.getKeys();
 | 
	
		
			
				|  |  | +      var vertex = vertices[polygon.vertexIds[i]];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    this.dVelocity.set(0, 0, 0);
 | 
	
		
			
				|  |  | -    if (data.enabled) {
 | 
	
		
			
				|  |  | -      if (keys.KeyW || keys.ArrowUp)    { this.dVelocity.z -= 1; }
 | 
	
		
			
				|  |  | -      if (keys.KeyA || keys.ArrowLeft)  { this.dVelocity.x -= 1; }
 | 
	
		
			
				|  |  | -      if (keys.KeyS || keys.ArrowDown)  { this.dVelocity.z += 1; }
 | 
	
		
			
				|  |  | -      if (keys.KeyD || keys.ArrowRight) { this.dVelocity.x += 1; }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +      var nextVertex, previousVertex;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    return this.dVelocity.clone();
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +      if (i === 0) {
 | 
	
		
			
				|  |  | +        nextVertex = vertices[polygon.vertexIds[1]];
 | 
	
		
			
				|  |  | +        previousVertex = vertices[polygon.vertexIds[polygon.vertexIds.length - 1]];
 | 
	
		
			
				|  |  | +      } else if (i === polygon.vertexIds.length - 1) {
 | 
	
		
			
				|  |  | +        nextVertex = vertices[polygon.vertexIds[0]];
 | 
	
		
			
				|  |  | +        previousVertex = vertices[polygon.vertexIds[polygon.vertexIds.length - 2]];
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        nextVertex = vertices[polygon.vertexIds[i + 1]];
 | 
	
		
			
				|  |  | +        previousVertex = vertices[polygon.vertexIds[i - 1]];
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /*******************************************************************
 | 
	
		
			
				|  |  | -  * Events
 | 
	
		
			
				|  |  | -  */
 | 
	
		
			
				|  |  | +      var a = nextVertex.clone().sub(vertex);
 | 
	
		
			
				|  |  | +      var b = previousVertex.clone().sub(vertex);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  play: function () {
 | 
	
		
			
				|  |  | -    this.attachEventListeners();
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +      var angle = a.angleTo(b);
 | 
	
		
			
				|  |  | +      total += angle;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  pause: function () {
 | 
	
		
			
				|  |  | -    this.removeEventListeners();
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +      if (angle === Math.PI || angle === 0) return false;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  remove: function () {
 | 
	
		
			
				|  |  | -    this.pause();
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +      var r = a.cross(b).y;
 | 
	
		
			
				|  |  | +      results.push(r);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  attachEventListeners: function () {
 | 
	
		
			
				|  |  | -    window.addEventListener('keydown', this.listeners.keydown, false);
 | 
	
		
			
				|  |  | -    window.addEventListener('keyup', this.listeners.keyup, false);
 | 
	
		
			
				|  |  | -    window.addEventListener('blur', this.listeners.blur, false);
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +    // if ( total > (polygon.vertexIds.length-2)*Math.PI ) return false;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  removeEventListeners: function () {
 | 
	
		
			
				|  |  | -    window.removeEventListener('keydown', this.listeners.keydown);
 | 
	
		
			
				|  |  | -    window.removeEventListener('keyup', this.listeners.keyup);
 | 
	
		
			
				|  |  | -    window.removeEventListener('blur', this.listeners.blur);
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +    results.forEach((r) => {
 | 
	
		
			
				|  |  | +      if (r === 0) convex = false;
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  onKeyDown: function (event) {
 | 
	
		
			
				|  |  | -    if (AFRAME.utils.shouldCaptureKeyEvent(event)) {
 | 
	
		
			
				|  |  | -      this.localKeys[event.code] = true;
 | 
	
		
			
				|  |  | -      this.emit(event);
 | 
	
		
			
				|  |  | +    if (results[0] > 0) {
 | 
	
		
			
				|  |  | +      results.forEach((r) => {
 | 
	
		
			
				|  |  | +        if (r < 0) convex = false;
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +      results.forEach((r) => {
 | 
	
		
			
				|  |  | +        if (r > 0) convex = false;
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  onKeyUp: function (event) {
 | 
	
		
			
				|  |  | -    if (AFRAME.utils.shouldCaptureKeyEvent(event)) {
 | 
	
		
			
				|  |  | -      delete this.localKeys[event.code];
 | 
	
		
			
				|  |  | -      this.emit(event);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +    return convex;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  onBlur: function () {
 | 
	
		
			
				|  |  | -    for (var code in this.localKeys) {
 | 
	
		
			
				|  |  | -      if (this.localKeys.hasOwnProperty(code)) {
 | 
	
		
			
				|  |  | -        delete this.localKeys[code];
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +  static distanceToSquared (a, b) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  emit: function (event) {
 | 
	
		
			
				|  |  | -    // TODO - keydown only initially?
 | 
	
		
			
				|  |  | -    // TODO - where the f is the spacebar
 | 
	
		
			
				|  |  | +    var dx = a.x - b.x;
 | 
	
		
			
				|  |  | +    var dy = a.y - b.y;
 | 
	
		
			
				|  |  | +    var dz = a.z - b.z;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    return dx * dx + dy * dy + dz * dz;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  //+ Jonas Raoni Soares Silva
 | 
	
		
			
				|  |  | +  //@ http://jsfromhell.com/math/is-point-in-poly [rev. #0]
 | 
	
		
			
				|  |  | +  static isPointInPoly (poly, pt) {
 | 
	
		
			
				|  |  | +    for (var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i)
 | 
	
		
			
				|  |  | +      ((poly[i].z <= pt.z && pt.z < poly[j].z) || (poly[j].z <= pt.z && pt.z < poly[i].z)) && (pt.x < (poly[j].x - poly[i].x) * (pt.z - poly[i].z) / (poly[j].z - poly[i].z) + poly[i].x) && (c = !c);
 | 
	
		
			
				|  |  | +    return c;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // Emit original event.
 | 
	
		
			
				|  |  | -    if (PROXY_FLAG in event) {
 | 
	
		
			
				|  |  | -      // TODO - Method never triggered.
 | 
	
		
			
				|  |  | -      this.el.emit(event.type, event);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +  static isVectorInPolygon (vector, polygon, vertices) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // Emit convenience event, identifying key.
 | 
	
		
			
				|  |  | -    this.el.emit(event.type + ':' + event.code, new KeyboardEvent(event.type, event));
 | 
	
		
			
				|  |  | -    if (this.data.debug) console.log(event.type + ':' + event.code);
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +    // reference point will be the centroid of the polygon
 | 
	
		
			
				|  |  | +    // We need to rotate the vector as well as all the points which the polygon uses
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /*******************************************************************
 | 
	
		
			
				|  |  | -  * Accessors
 | 
	
		
			
				|  |  | -  */
 | 
	
		
			
				|  |  | +    var lowestPoint = 100000;
 | 
	
		
			
				|  |  | +    var highestPoint = -100000;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  isPressed: function (code) {
 | 
	
		
			
				|  |  | -    return code in this.getKeys();
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +    var polygonVertices = [];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  getKeys: function () {
 | 
	
		
			
				|  |  | -    if (this.isProxied()) {
 | 
	
		
			
				|  |  | -      return this.el.sceneEl.components['proxy-controls'].getKeyboard();
 | 
	
		
			
				|  |  | +    polygon.vertexIds.forEach((vId) => {
 | 
	
		
			
				|  |  | +      lowestPoint = Math.min(vertices[vId].y, lowestPoint);
 | 
	
		
			
				|  |  | +      highestPoint = Math.max(vertices[vId].y, highestPoint);
 | 
	
		
			
				|  |  | +      polygonVertices.push(vertices[vId]);
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (vector.y < highestPoint + 0.5 && vector.y > lowestPoint - 0.5 &&
 | 
	
		
			
				|  |  | +      this.isPointInPoly(polygonVertices, vector)) {
 | 
	
		
			
				|  |  | +      return true;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -    return this.localKeys;
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +    return false;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  isProxied: function () {
 | 
	
		
			
				|  |  | -    var proxyControls = this.el.sceneEl.components['proxy-controls'];
 | 
	
		
			
				|  |  | -    return proxyControls && proxyControls.isConnected();
 | 
	
		
			
				|  |  | +  static triarea2 (a, b, c) {
 | 
	
		
			
				|  |  | +    var ax = b.x - a.x;
 | 
	
		
			
				|  |  | +    var az = b.z - a.z;
 | 
	
		
			
				|  |  | +    var bx = c.x - a.x;
 | 
	
		
			
				|  |  | +    var bz = c.z - a.z;
 | 
	
		
			
				|  |  | +    return bx * az - ax * bz;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | +  static vequal (a, b) {
 | 
	
		
			
				|  |  | +    return this.distanceToSquared(a, b) < 0.00001;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{"../../lib/keyboard.polyfill":10}],86:[function(require,module,exports){
 | 
	
		
			
				|  |  | -document.exitPointerLock = document.exitPointerLock || document.mozExitPointerLock;
 | 
	
		
			
				|  |  | +  static array_intersect () {
 | 
	
		
			
				|  |  | +    let i, shortest, nShortest, n, len, ret = [],
 | 
	
		
			
				|  |  | +      obj = {},
 | 
	
		
			
				|  |  | +      nOthers;
 | 
	
		
			
				|  |  | +    nOthers = arguments.length - 1;
 | 
	
		
			
				|  |  | +    nShortest = arguments[0].length;
 | 
	
		
			
				|  |  | +    shortest = 0;
 | 
	
		
			
				|  |  | +    for (i = 0; i <= nOthers; i++) {
 | 
	
		
			
				|  |  | +      n = arguments[i].length;
 | 
	
		
			
				|  |  | +      if (n < nShortest) {
 | 
	
		
			
				|  |  | +        shortest = i;
 | 
	
		
			
				|  |  | +        nShortest = n;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * Mouse + Pointerlock controls.
 | 
	
		
			
				|  |  | - *
 | 
	
		
			
				|  |  | - * Based on: https://github.com/aframevr/aframe/pull/1056
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -module.exports = {
 | 
	
		
			
				|  |  | -  schema: {
 | 
	
		
			
				|  |  | -    enabled: { default: true },
 | 
	
		
			
				|  |  | -    pointerlockEnabled: { default: true },
 | 
	
		
			
				|  |  | -    sensitivity: { default: 1 / 25 }
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +    for (i = 0; i <= nOthers; i++) {
 | 
	
		
			
				|  |  | +      n = (i === shortest) ? 0 : (i || shortest); //Read the shortest array first. Read the first array instead of the shortest
 | 
	
		
			
				|  |  | +      len = arguments[n].length;
 | 
	
		
			
				|  |  | +      for (var j = 0; j < len; j++) {
 | 
	
		
			
				|  |  | +        var elem = arguments[n][j];
 | 
	
		
			
				|  |  | +        if (obj[elem] === i - 1) {
 | 
	
		
			
				|  |  | +          if (i === nOthers) {
 | 
	
		
			
				|  |  | +            ret.push(elem);
 | 
	
		
			
				|  |  | +            obj[elem] = 0;
 | 
	
		
			
				|  |  | +          } else {
 | 
	
		
			
				|  |  | +            obj[elem] = i;
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  | +        } else if (i === 0) {
 | 
	
		
			
				|  |  | +          obj[elem] = 0;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    return ret;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  init: function () {
 | 
	
		
			
				|  |  | -    this.mouseDown = false;
 | 
	
		
			
				|  |  | -    this.pointerLocked = false;
 | 
	
		
			
				|  |  | -    this.lookVector = new THREE.Vector2();
 | 
	
		
			
				|  |  | -    this.bindMethods();
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  update: function (previousData) {
 | 
	
		
			
				|  |  | -    var data = this.data;
 | 
	
		
			
				|  |  | -    if (previousData.pointerlockEnabled && !data.pointerlockEnabled && this.pointerLocked) {
 | 
	
		
			
				|  |  | -      document.exitPointerLock();
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  play: function () {
 | 
	
		
			
				|  |  | -    this.addEventListeners();
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +module.exports = Utils;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  pause: function () {
 | 
	
		
			
				|  |  | -    this.removeEventListeners();
 | 
	
		
			
				|  |  | -    this.lookVector.set(0, 0);
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +},{}],84:[function(require,module,exports){
 | 
	
		
			
				|  |  | +var CANNON = require('cannon'),
 | 
	
		
			
				|  |  | +    quickhull = require('./lib/THREE.quickhull');
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  remove: function () {
 | 
	
		
			
				|  |  | -    this.pause();
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +var PI_2 = Math.PI / 2;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  bindMethods: function () {
 | 
	
		
			
				|  |  | -    this.onMouseDown = this.onMouseDown.bind(this);
 | 
	
		
			
				|  |  | -    this.onMouseMove = this.onMouseMove.bind(this);
 | 
	
		
			
				|  |  | -    this.onMouseUp = this.onMouseUp.bind(this);
 | 
	
		
			
				|  |  | -    this.onMouseUp = this.onMouseUp.bind(this);
 | 
	
		
			
				|  |  | -    this.onPointerLockChange = this.onPointerLockChange.bind(this);
 | 
	
		
			
				|  |  | -    this.onPointerLockChange = this.onPointerLockChange.bind(this);
 | 
	
		
			
				|  |  | -    this.onPointerLockChange = this.onPointerLockChange.bind(this);
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +var Type = {
 | 
	
		
			
				|  |  | +  BOX: 'Box',
 | 
	
		
			
				|  |  | +  CYLINDER: 'Cylinder',
 | 
	
		
			
				|  |  | +  SPHERE: 'Sphere',
 | 
	
		
			
				|  |  | +  HULL: 'ConvexPolyhedron',
 | 
	
		
			
				|  |  | +  MESH: 'Trimesh'
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  addEventListeners: function () {
 | 
	
		
			
				|  |  | -    var sceneEl = this.el.sceneEl;
 | 
	
		
			
				|  |  | -    var canvasEl = sceneEl.canvas;
 | 
	
		
			
				|  |  | -    var data = this.data;
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Given a THREE.Object3D instance, creates a corresponding CANNON shape.
 | 
	
		
			
				|  |  | + * @param  {THREE.Object3D} object
 | 
	
		
			
				|  |  | + * @return {CANNON.Shape}
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +module.exports = CANNON.mesh2shape = function (object, options) {
 | 
	
		
			
				|  |  | +  options = options || {};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (!canvasEl) {
 | 
	
		
			
				|  |  | -      sceneEl.addEventListener('render-target-loaded', this.addEventListeners.bind(this));
 | 
	
		
			
				|  |  | -      return;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +  var geometry;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    canvasEl.addEventListener('mousedown', this.onMouseDown, false);
 | 
	
		
			
				|  |  | -    canvasEl.addEventListener('mousemove', this.onMouseMove, false);
 | 
	
		
			
				|  |  | -    canvasEl.addEventListener('mouseup', this.onMouseUp, false);
 | 
	
		
			
				|  |  | -    canvasEl.addEventListener('mouseout', this.onMouseUp, false);
 | 
	
		
			
				|  |  | +  if (options.type === Type.BOX) {
 | 
	
		
			
				|  |  | +    return createBoundingBoxShape(object);
 | 
	
		
			
				|  |  | +  } else if (options.type === Type.CYLINDER) {
 | 
	
		
			
				|  |  | +    return createBoundingCylinderShape(object, options);
 | 
	
		
			
				|  |  | +  } else if (options.type === Type.SPHERE) {
 | 
	
		
			
				|  |  | +    return createBoundingSphereShape(object, options);
 | 
	
		
			
				|  |  | +  } else if (options.type === Type.HULL) {
 | 
	
		
			
				|  |  | +    return createConvexPolyhedron(object);
 | 
	
		
			
				|  |  | +  } else if (options.type === Type.MESH) {
 | 
	
		
			
				|  |  | +    geometry = getGeometry(object);
 | 
	
		
			
				|  |  | +    return geometry ? createTrimeshShape(geometry) : null;
 | 
	
		
			
				|  |  | +  } else if (options.type) {
 | 
	
		
			
				|  |  | +    throw new Error('[CANNON.mesh2shape] Invalid type "%s".', options.type);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (data.pointerlockEnabled) {
 | 
	
		
			
				|  |  | -      document.addEventListener('pointerlockchange', this.onPointerLockChange, false);
 | 
	
		
			
				|  |  | -      document.addEventListener('mozpointerlockchange', this.onPointerLockChange, false);
 | 
	
		
			
				|  |  | -      document.addEventListener('pointerlockerror', this.onPointerLockError, false);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +  geometry = getGeometry(object);
 | 
	
		
			
				|  |  | +  if (!geometry) return null;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  removeEventListeners: function () {
 | 
	
		
			
				|  |  | -    var canvasEl = this.el.sceneEl && this.el.sceneEl.canvas;
 | 
	
		
			
				|  |  | -    if (canvasEl) {
 | 
	
		
			
				|  |  | -      canvasEl.removeEventListener('mousedown', this.onMouseDown, false);
 | 
	
		
			
				|  |  | -      canvasEl.removeEventListener('mousemove', this.onMouseMove, false);
 | 
	
		
			
				|  |  | -      canvasEl.removeEventListener('mouseup', this.onMouseUp, false);
 | 
	
		
			
				|  |  | -      canvasEl.removeEventListener('mouseout', this.onMouseUp, false);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    document.removeEventListener('pointerlockchange', this.onPointerLockChange, false);
 | 
	
		
			
				|  |  | -    document.removeEventListener('mozpointerlockchange', this.onPointerLockChange, false);
 | 
	
		
			
				|  |  | -    document.removeEventListener('pointerlockerror', this.onPointerLockError, false);
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +  var type = geometry.metadata
 | 
	
		
			
				|  |  | +    ? geometry.metadata.type
 | 
	
		
			
				|  |  | +    : geometry.type;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  isRotationActive: function () {
 | 
	
		
			
				|  |  | -    return this.data.enabled && (this.mouseDown || this.pointerLocked);
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +  switch (type) {
 | 
	
		
			
				|  |  | +    case 'BoxGeometry':
 | 
	
		
			
				|  |  | +    case 'BoxBufferGeometry':
 | 
	
		
			
				|  |  | +      return createBoxShape(geometry);
 | 
	
		
			
				|  |  | +    case 'CylinderGeometry':
 | 
	
		
			
				|  |  | +    case 'CylinderBufferGeometry':
 | 
	
		
			
				|  |  | +      return createCylinderShape(geometry);
 | 
	
		
			
				|  |  | +    case 'PlaneGeometry':
 | 
	
		
			
				|  |  | +    case 'PlaneBufferGeometry':
 | 
	
		
			
				|  |  | +      return createPlaneShape(geometry);
 | 
	
		
			
				|  |  | +    case 'SphereGeometry':
 | 
	
		
			
				|  |  | +    case 'SphereBufferGeometry':
 | 
	
		
			
				|  |  | +      return createSphereShape(geometry);
 | 
	
		
			
				|  |  | +    case 'TubeGeometry':
 | 
	
		
			
				|  |  | +    case 'Geometry':
 | 
	
		
			
				|  |  | +    case 'BufferGeometry':
 | 
	
		
			
				|  |  | +      return createBoundingBoxShape(object);
 | 
	
		
			
				|  |  | +    default:
 | 
	
		
			
				|  |  | +      console.warn('Unrecognized geometry: "%s". Using bounding box as shape.', geometry.type);
 | 
	
		
			
				|  |  | +      return createBoxShape(geometry);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /**
 | 
	
		
			
				|  |  | -   * Returns the sum of all mouse movement since last call.
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | -  getRotationDelta: function () {
 | 
	
		
			
				|  |  | -    var dRotation = this.lookVector.clone().multiplyScalar(this.data.sensitivity);
 | 
	
		
			
				|  |  | -    this.lookVector.set(0, 0);
 | 
	
		
			
				|  |  | -    return dRotation;
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +CANNON.mesh2shape.Type = Type;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  onMouseMove: function (event) {
 | 
	
		
			
				|  |  | -    var previousMouseEvent = this.previousMouseEvent;
 | 
	
		
			
				|  |  | +/******************************************************************************
 | 
	
		
			
				|  |  | + * Shape construction
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (!this.data.enabled || !(this.mouseDown || this.pointerLocked)) {
 | 
	
		
			
				|  |  | -      return;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | + /**
 | 
	
		
			
				|  |  | +  * @param  {THREE.Geometry} geometry
 | 
	
		
			
				|  |  | +  * @return {CANNON.Shape}
 | 
	
		
			
				|  |  | +  */
 | 
	
		
			
				|  |  | + function createBoxShape (geometry) {
 | 
	
		
			
				|  |  | +   var vertices = getVertices(geometry);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    var movementX = event.movementX || event.mozMovementX || 0;
 | 
	
		
			
				|  |  | -    var movementY = event.movementY || event.mozMovementY || 0;
 | 
	
		
			
				|  |  | +   if (!vertices.length) return null;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +   geometry.computeBoundingBox();
 | 
	
		
			
				|  |  | +   var box = geometry.boundingBox;
 | 
	
		
			
				|  |  | +   return new CANNON.Box(new CANNON.Vec3(
 | 
	
		
			
				|  |  | +     (box.max.x - box.min.x) / 2,
 | 
	
		
			
				|  |  | +     (box.max.y - box.min.y) / 2,
 | 
	
		
			
				|  |  | +     (box.max.z - box.min.z) / 2
 | 
	
		
			
				|  |  | +   ));
 | 
	
		
			
				|  |  | + }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (!this.pointerLocked) {
 | 
	
		
			
				|  |  | -      movementX = event.screenX - previousMouseEvent.screenX;
 | 
	
		
			
				|  |  | -      movementY = event.screenY - previousMouseEvent.screenY;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Bounding box needs to be computed with the entire mesh, not just geometry.
 | 
	
		
			
				|  |  | + * @param  {THREE.Object3D} mesh
 | 
	
		
			
				|  |  | + * @return {CANNON.Shape}
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +function createBoundingBoxShape (object) {
 | 
	
		
			
				|  |  | +  var shape, localPosition, worldPosition,
 | 
	
		
			
				|  |  | +      box = new THREE.Box3();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    this.lookVector.x += movementX;
 | 
	
		
			
				|  |  | -    this.lookVector.y += movementY;
 | 
	
		
			
				|  |  | +  box.setFromObject(object);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    this.previousMouseEvent = event;
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +  if (!isFinite(box.min.lengthSq())) return null;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  onMouseDown: function (event) {
 | 
	
		
			
				|  |  | -    var canvasEl = this.el.sceneEl.canvas,
 | 
	
		
			
				|  |  | -        isEditing = (AFRAME.INSPECTOR || {}).opened;
 | 
	
		
			
				|  |  | +  shape = new CANNON.Box(new CANNON.Vec3(
 | 
	
		
			
				|  |  | +    (box.max.x - box.min.x) / 2,
 | 
	
		
			
				|  |  | +    (box.max.y - box.min.y) / 2,
 | 
	
		
			
				|  |  | +    (box.max.z - box.min.z) / 2
 | 
	
		
			
				|  |  | +  ));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    this.mouseDown = true;
 | 
	
		
			
				|  |  | -    this.previousMouseEvent = event;
 | 
	
		
			
				|  |  | +  object.updateMatrixWorld();
 | 
	
		
			
				|  |  | +  worldPosition = new THREE.Vector3();
 | 
	
		
			
				|  |  | +  worldPosition.setFromMatrixPosition(object.matrixWorld);
 | 
	
		
			
				|  |  | +  localPosition = box.translate(worldPosition.negate()).getCenter();
 | 
	
		
			
				|  |  | +  if (localPosition.lengthSq()) {
 | 
	
		
			
				|  |  | +    shape.offset = localPosition;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (this.data.pointerlockEnabled && !this.pointerLocked && !isEditing) {
 | 
	
		
			
				|  |  | -      if (canvasEl.requestPointerLock) {
 | 
	
		
			
				|  |  | -        canvasEl.requestPointerLock();
 | 
	
		
			
				|  |  | -      } else if (canvasEl.mozRequestPointerLock) {
 | 
	
		
			
				|  |  | -        canvasEl.mozRequestPointerLock();
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +  return shape;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  onMouseUp: function () {
 | 
	
		
			
				|  |  | -    this.mouseDown = false;
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Computes 3D convex hull as a CANNON.ConvexPolyhedron.
 | 
	
		
			
				|  |  | + * @param  {THREE.Object3D} mesh
 | 
	
		
			
				|  |  | + * @return {CANNON.Shape}
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +function createConvexPolyhedron (object) {
 | 
	
		
			
				|  |  | +  var i, vertices, faces, hull,
 | 
	
		
			
				|  |  | +      eps = 1e-4,
 | 
	
		
			
				|  |  | +      geometry = getGeometry(object);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  onPointerLockChange: function () {
 | 
	
		
			
				|  |  | -    this.pointerLocked = !!(document.pointerLockElement || document.mozPointerLockElement);
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +  if (!geometry || !geometry.vertices.length) return null;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  onPointerLockError: function () {
 | 
	
		
			
				|  |  | -    this.pointerLocked = false;
 | 
	
		
			
				|  |  | +  // Perturb.
 | 
	
		
			
				|  |  | +  for (i = 0; i < geometry.vertices.length; i++) {
 | 
	
		
			
				|  |  | +    geometry.vertices[i].x += (Math.random() - 0.5) * eps;
 | 
	
		
			
				|  |  | +    geometry.vertices[i].y += (Math.random() - 0.5) * eps;
 | 
	
		
			
				|  |  | +    geometry.vertices[i].z += (Math.random() - 0.5) * eps;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -},{}],87:[function(require,module,exports){
 | 
	
		
			
				|  |  | -module.exports = {
 | 
	
		
			
				|  |  | -  schema: {
 | 
	
		
			
				|  |  | -    enabled: { default: true }
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  init: function () {
 | 
	
		
			
				|  |  | -    this.dVelocity = new THREE.Vector3();
 | 
	
		
			
				|  |  | -    this.bindMethods();
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +  // Compute the 3D convex hull.
 | 
	
		
			
				|  |  | +  hull = quickhull(geometry);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  play: function () {
 | 
	
		
			
				|  |  | -    this.addEventListeners();
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +  // Convert from THREE.Vector3 to CANNON.Vec3.
 | 
	
		
			
				|  |  | +  vertices = new Array(hull.vertices.length);
 | 
	
		
			
				|  |  | +  for (i = 0; i < hull.vertices.length; i++) {
 | 
	
		
			
				|  |  | +    vertices[i] = new CANNON.Vec3(hull.vertices[i].x, hull.vertices[i].y, hull.vertices[i].z);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  pause: function () {
 | 
	
		
			
				|  |  | -    this.removeEventListeners();
 | 
	
		
			
				|  |  | -    this.dVelocity.set(0, 0, 0);
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +  // Convert from THREE.Face to Array<number>.
 | 
	
		
			
				|  |  | +  faces = new Array(hull.faces.length);
 | 
	
		
			
				|  |  | +  for (i = 0; i < hull.faces.length; i++) {
 | 
	
		
			
				|  |  | +    faces[i] = [hull.faces[i].a, hull.faces[i].b, hull.faces[i].c];
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  remove: function () {
 | 
	
		
			
				|  |  | -    this.pause();
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +  return new CANNON.ConvexPolyhedron(vertices, faces);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  addEventListeners: function () {
 | 
	
		
			
				|  |  | -    var sceneEl = this.el.sceneEl;
 | 
	
		
			
				|  |  | -    var canvasEl = sceneEl.canvas;
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * @param  {THREE.Geometry} geometry
 | 
	
		
			
				|  |  | + * @return {CANNON.Shape}
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +function createCylinderShape (geometry) {
 | 
	
		
			
				|  |  | +  var shape,
 | 
	
		
			
				|  |  | +      params = geometry.metadata
 | 
	
		
			
				|  |  | +        ? geometry.metadata.parameters
 | 
	
		
			
				|  |  | +        : geometry.parameters;
 | 
	
		
			
				|  |  | +  shape = new CANNON.Cylinder(
 | 
	
		
			
				|  |  | +    params.radiusTop,
 | 
	
		
			
				|  |  | +    params.radiusBottom,
 | 
	
		
			
				|  |  | +    params.height,
 | 
	
		
			
				|  |  | +    params.radialSegments
 | 
	
		
			
				|  |  | +  );
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (!canvasEl) {
 | 
	
		
			
				|  |  | -      sceneEl.addEventListener('render-target-loaded', this.addEventListeners.bind(this));
 | 
	
		
			
				|  |  | -      return;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +  // Include metadata for serialization.
 | 
	
		
			
				|  |  | +  shape._type = CANNON.Shape.types.CYLINDER; // Patch schteppe/cannon.js#329.
 | 
	
		
			
				|  |  | +  shape.radiusTop = params.radiusTop;
 | 
	
		
			
				|  |  | +  shape.radiusBottom = params.radiusBottom;
 | 
	
		
			
				|  |  | +  shape.height = params.height;
 | 
	
		
			
				|  |  | +  shape.numSegments = params.radialSegments;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    canvasEl.addEventListener('touchstart', this.onTouchStart);
 | 
	
		
			
				|  |  | -    canvasEl.addEventListener('touchend', this.onTouchEnd);
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +  shape.orientation = new CANNON.Quaternion();
 | 
	
		
			
				|  |  | +  shape.orientation.setFromEuler(THREE.Math.degToRad(-90), 0, 0, 'XYZ').normalize();
 | 
	
		
			
				|  |  | +  return shape;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  removeEventListeners: function () {
 | 
	
		
			
				|  |  | -    var canvasEl = this.el.sceneEl && this.el.sceneEl.canvas;
 | 
	
		
			
				|  |  | -    if (!canvasEl) { return; }
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * @param  {THREE.Object3D} object
 | 
	
		
			
				|  |  | + * @return {CANNON.Shape}
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +function createBoundingCylinderShape (object, options) {
 | 
	
		
			
				|  |  | +  var shape, height, radius,
 | 
	
		
			
				|  |  | +      box = new THREE.Box3(),
 | 
	
		
			
				|  |  | +      axes = ['x', 'y', 'z'],
 | 
	
		
			
				|  |  | +      majorAxis = options.cylinderAxis || 'y',
 | 
	
		
			
				|  |  | +      minorAxes = axes.splice(axes.indexOf(majorAxis), 1) && axes;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    canvasEl.removeEventListener('touchstart', this.onTouchStart);
 | 
	
		
			
				|  |  | -    canvasEl.removeEventListener('touchend', this.onTouchEnd);
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +  box.setFromObject(object);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  isVelocityActive: function () {
 | 
	
		
			
				|  |  | -    return this.data.enabled && this.isMoving;
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +  if (!isFinite(box.min.lengthSq())) return null;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  getVelocityDelta: function () {
 | 
	
		
			
				|  |  | -    this.dVelocity.z = this.isMoving ? -1 : 0;
 | 
	
		
			
				|  |  | -    return this.dVelocity.clone();
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +  // Compute cylinder dimensions.
 | 
	
		
			
				|  |  | +  height = box.max[majorAxis] - box.min[majorAxis];
 | 
	
		
			
				|  |  | +  radius = 0.5 * Math.max(
 | 
	
		
			
				|  |  | +    box.max[minorAxes[0]] - box.min[minorAxes[0]],
 | 
	
		
			
				|  |  | +    box.max[minorAxes[1]] - box.min[minorAxes[1]]
 | 
	
		
			
				|  |  | +  );
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  bindMethods: function () {
 | 
	
		
			
				|  |  | -    this.onTouchStart = this.onTouchStart.bind(this);
 | 
	
		
			
				|  |  | -    this.onTouchEnd = this.onTouchEnd.bind(this);
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +  // Create shape.
 | 
	
		
			
				|  |  | +  shape = new CANNON.Cylinder(radius, radius, height, 12);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  onTouchStart: function (e) {
 | 
	
		
			
				|  |  | -    this.isMoving = true;
 | 
	
		
			
				|  |  | -    e.preventDefault();
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +  // Include metadata for serialization.
 | 
	
		
			
				|  |  | +  shape._type = CANNON.Shape.types.CYLINDER; // Patch schteppe/cannon.js#329.
 | 
	
		
			
				|  |  | +  shape.radiusTop = radius;
 | 
	
		
			
				|  |  | +  shape.radiusBottom = radius;
 | 
	
		
			
				|  |  | +  shape.height = height;
 | 
	
		
			
				|  |  | +  shape.numSegments = 12;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  onTouchEnd: function (e) {
 | 
	
		
			
				|  |  | -    this.isMoving = false;
 | 
	
		
			
				|  |  | -    e.preventDefault();
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | +  shape.orientation = new CANNON.Quaternion();
 | 
	
		
			
				|  |  | +  shape.orientation.setFromEuler(
 | 
	
		
			
				|  |  | +    majorAxis === 'y' ? PI_2 : 0,
 | 
	
		
			
				|  |  | +    majorAxis === 'z' ? PI_2 : 0,
 | 
	
		
			
				|  |  | +    0,
 | 
	
		
			
				|  |  | +    'XYZ'
 | 
	
		
			
				|  |  | +  ).normalize();
 | 
	
		
			
				|  |  | +  return shape;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{}],88:[function(require,module,exports){
 | 
	
		
			
				|  |  |  /**
 | 
	
		
			
				|  |  | - * Universal Controls
 | 
	
		
			
				|  |  | - *
 | 
	
		
			
				|  |  | - * @author Don McCurdy <dm@donmccurdy.com>
 | 
	
		
			
				|  |  | + * @param  {THREE.Geometry} geometry
 | 
	
		
			
				|  |  | + * @return {CANNON.Shape}
 | 
	
		
			
				|  |  |   */
 | 
	
		
			
				|  |  | +function createPlaneShape (geometry) {
 | 
	
		
			
				|  |  | +  geometry.computeBoundingBox();
 | 
	
		
			
				|  |  | +  var box = geometry.boundingBox;
 | 
	
		
			
				|  |  | +  return new CANNON.Box(new CANNON.Vec3(
 | 
	
		
			
				|  |  | +    (box.max.x - box.min.x) / 2 || 0.1,
 | 
	
		
			
				|  |  | +    (box.max.y - box.min.y) / 2 || 0.1,
 | 
	
		
			
				|  |  | +    (box.max.z - box.min.z) / 2 || 0.1
 | 
	
		
			
				|  |  | +  ));
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -var COMPONENT_SUFFIX = '-controls',
 | 
	
		
			
				|  |  | -    MAX_DELTA = 0.2, // ms
 | 
	
		
			
				|  |  | -    PI_2 = Math.PI / 2;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -module.exports = {
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  /*******************************************************************
 | 
	
		
			
				|  |  | -   * Schema
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  dependencies: ['velocity', 'rotation'],
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * @param  {THREE.Geometry} geometry
 | 
	
		
			
				|  |  | + * @return {CANNON.Shape}
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +function createSphereShape (geometry) {
 | 
	
		
			
				|  |  | +  var params = geometry.metadata
 | 
	
		
			
				|  |  | +    ? geometry.metadata.parameters
 | 
	
		
			
				|  |  | +    : geometry.parameters;
 | 
	
		
			
				|  |  | +  return new CANNON.Sphere(params.radius);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  schema: {
 | 
	
		
			
				|  |  | -    enabled:              { default: true },
 | 
	
		
			
				|  |  | -    movementEnabled:      { default: true },
 | 
	
		
			
				|  |  | -    movementControls:     { default: ['gamepad', 'keyboard', 'touch', 'hmd'] },
 | 
	
		
			
				|  |  | -    rotationEnabled:      { default: true },
 | 
	
		
			
				|  |  | -    rotationControls:     { default: ['hmd', 'gamepad', 'mouse'] },
 | 
	
		
			
				|  |  | -    movementSpeed:        { default: 5 }, // m/s
 | 
	
		
			
				|  |  | -    movementEasing:       { default: 15 }, // m/s2
 | 
	
		
			
				|  |  | -    movementEasingY:      { default: 0  }, // m/s2
 | 
	
		
			
				|  |  | -    movementAcceleration: { default: 80 }, // m/s2
 | 
	
		
			
				|  |  | -    rotationSensitivity:  { default: 0.05 }, // radians/frame, ish
 | 
	
		
			
				|  |  | -    fly:                  { default: false },
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * @param  {THREE.Object3D} object
 | 
	
		
			
				|  |  | + * @return {CANNON.Shape}
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +function createBoundingSphereShape (object, options) {
 | 
	
		
			
				|  |  | +  if (options.sphereRadius) {
 | 
	
		
			
				|  |  | +    return new CANNON.Sphere(options.sphereRadius);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  var geometry = getGeometry(object);
 | 
	
		
			
				|  |  | +  if (!geometry) return null;
 | 
	
		
			
				|  |  | +  geometry.computeBoundingSphere();
 | 
	
		
			
				|  |  | +  return new CANNON.Sphere(geometry.boundingSphere.radius);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /*******************************************************************
 | 
	
		
			
				|  |  | -   * Lifecycle
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * @param  {THREE.Geometry} geometry
 | 
	
		
			
				|  |  | + * @return {CANNON.Shape}
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +function createTrimeshShape (geometry) {
 | 
	
		
			
				|  |  | +  var indices,
 | 
	
		
			
				|  |  | +      vertices = getVertices(geometry);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  init: function () {
 | 
	
		
			
				|  |  | -    var rotation = this.el.getAttribute('rotation');
 | 
	
		
			
				|  |  | +  if (!vertices.length) return null;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (this.el.hasAttribute('look-controls') && this.data.rotationEnabled) {
 | 
	
		
			
				|  |  | -      console.error('[universal-controls] The `universal-controls` component is a replacement '
 | 
	
		
			
				|  |  | -        + 'for `look-controls`, and cannot be used in combination with it.');
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +  indices = Object.keys(vertices).map(Number);
 | 
	
		
			
				|  |  | +  return new CANNON.Trimesh(vertices, indices);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // Movement
 | 
	
		
			
				|  |  | -    this.velocity = new THREE.Vector3();
 | 
	
		
			
				|  |  | +/******************************************************************************
 | 
	
		
			
				|  |  | + * Utils
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // Rotation
 | 
	
		
			
				|  |  | -    this.pitch = new THREE.Object3D();
 | 
	
		
			
				|  |  | -    this.pitch.rotation.x = THREE.Math.degToRad(rotation.x);
 | 
	
		
			
				|  |  | -    this.yaw = new THREE.Object3D();
 | 
	
		
			
				|  |  | -    this.yaw.position.y = 10;
 | 
	
		
			
				|  |  | -    this.yaw.rotation.y = THREE.Math.degToRad(rotation.y);
 | 
	
		
			
				|  |  | -    this.yaw.add(this.pitch);
 | 
	
		
			
				|  |  | -    this.heading = new THREE.Euler(0, 0, 0, 'YXZ');
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Returns a single geometry for the given object. If the object is compound,
 | 
	
		
			
				|  |  | + * its geometries are automatically merged.
 | 
	
		
			
				|  |  | + * @param {THREE.Object3D} object
 | 
	
		
			
				|  |  | + * @return {THREE.Geometry}
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +function getGeometry (object) {
 | 
	
		
			
				|  |  | +  var matrix, mesh,
 | 
	
		
			
				|  |  | +      meshes = getMeshes(object),
 | 
	
		
			
				|  |  | +      tmp = new THREE.Geometry(),
 | 
	
		
			
				|  |  | +      combined = new THREE.Geometry();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (this.el.sceneEl.hasLoaded) {
 | 
	
		
			
				|  |  | -      this.injectControls();
 | 
	
		
			
				|  |  | +  if (meshes.length === 0) return null;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // Apply scale  – it can't easily be applied to a CANNON.Shape later.
 | 
	
		
			
				|  |  | +  if (meshes.length === 1) {
 | 
	
		
			
				|  |  | +    var position = new THREE.Vector3(),
 | 
	
		
			
				|  |  | +        quaternion = new THREE.Quaternion(),
 | 
	
		
			
				|  |  | +        scale = new THREE.Vector3();
 | 
	
		
			
				|  |  | +    if (meshes[0].geometry.isBufferGeometry) {
 | 
	
		
			
				|  |  | +      if (meshes[0].geometry.attributes.position) {
 | 
	
		
			
				|  |  | +        tmp.fromBufferGeometry(meshes[0].geometry);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |      } else {
 | 
	
		
			
				|  |  | -      this.el.sceneEl.addEventListener('loaded', this.injectControls.bind(this));
 | 
	
		
			
				|  |  | +      tmp = meshes[0].geometry.clone();
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +    tmp.metadata = meshes[0].geometry.metadata;
 | 
	
		
			
				|  |  | +    meshes[0].updateMatrixWorld();
 | 
	
		
			
				|  |  | +    meshes[0].matrixWorld.decompose(position, quaternion, scale);
 | 
	
		
			
				|  |  | +    return tmp.scale(scale.x, scale.y, scale.z);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  update: function () {
 | 
	
		
			
				|  |  | -    if (this.el.sceneEl.hasLoaded) {
 | 
	
		
			
				|  |  | -      this.injectControls();
 | 
	
		
			
				|  |  | +  // Recursively merge geometry, preserving local transforms.
 | 
	
		
			
				|  |  | +  while ((mesh = meshes.pop())) {
 | 
	
		
			
				|  |  | +    mesh.updateMatrixWorld();
 | 
	
		
			
				|  |  | +    if (mesh.geometry.isBufferGeometry) {
 | 
	
		
			
				|  |  | +      tmp.fromBufferGeometry(mesh.geometry);
 | 
	
		
			
				|  |  | +      combined.merge(tmp, mesh.matrixWorld);
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +      combined.merge(mesh.geometry, mesh.matrixWorld);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  injectControls: function () {
 | 
	
		
			
				|  |  | -    var i, name,
 | 
	
		
			
				|  |  | -        data = this.data;
 | 
	
		
			
				|  |  | +  matrix = new THREE.Matrix4();
 | 
	
		
			
				|  |  | +  matrix.scale(object.scale);
 | 
	
		
			
				|  |  | +  combined.applyMatrix(matrix);
 | 
	
		
			
				|  |  | +  return combined;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    for (i = 0; i < data.movementControls.length; i++) {
 | 
	
		
			
				|  |  | -      name = data.movementControls[i] + COMPONENT_SUFFIX;
 | 
	
		
			
				|  |  | -      if (!this.el.components[name]) {
 | 
	
		
			
				|  |  | -        this.el.setAttribute(name, '');
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * @param  {THREE.Geometry} geometry
 | 
	
		
			
				|  |  | + * @return {Array<number>}
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +function getVertices (geometry) {
 | 
	
		
			
				|  |  | +  if (!geometry.attributes) {
 | 
	
		
			
				|  |  | +    geometry = new THREE.BufferGeometry().fromGeometry(geometry);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  return (geometry.attributes.position || {}).array || [];
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    for (i = 0; i < data.rotationControls.length; i++) {
 | 
	
		
			
				|  |  | -      name = data.rotationControls[i] + COMPONENT_SUFFIX;
 | 
	
		
			
				|  |  | -      if (!this.el.components[name]) {
 | 
	
		
			
				|  |  | -        this.el.setAttribute(name, '');
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Returns a flat array of THREE.Mesh instances from the given object. If
 | 
	
		
			
				|  |  | + * nested transformations are found, they are applied to child meshes
 | 
	
		
			
				|  |  | + * as mesh.userData.matrix, so that each mesh has its position/rotation/scale
 | 
	
		
			
				|  |  | + * independently of all of its parents except the top-level object.
 | 
	
		
			
				|  |  | + * @param  {THREE.Object3D} object
 | 
	
		
			
				|  |  | + * @return {Array<THREE.Mesh>}
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +function getMeshes (object) {
 | 
	
		
			
				|  |  | +  var meshes = [];
 | 
	
		
			
				|  |  | +  object.traverse(function (o) {
 | 
	
		
			
				|  |  | +    if (o.type === 'Mesh') {
 | 
	
		
			
				|  |  | +      meshes.push(o);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +  });
 | 
	
		
			
				|  |  | +  return meshes;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /*******************************************************************
 | 
	
		
			
				|  |  | -   * Tick
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | +},{"./lib/THREE.quickhull":85,"cannon":23}],85:[function(require,module,exports){
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  tick: function (t, dt) {
 | 
	
		
			
				|  |  | -    if (!dt) { return; }
 | 
	
		
			
				|  |  | +  QuickHull
 | 
	
		
			
				|  |  | +  ---------
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // Update rotation.
 | 
	
		
			
				|  |  | -    if (this.data.rotationEnabled) this.updateRotation(dt);
 | 
	
		
			
				|  |  | +  The MIT License
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // Update velocity. If FPS is too low, reset.
 | 
	
		
			
				|  |  | -    if (this.data.movementEnabled && dt / 1000 > MAX_DELTA) {
 | 
	
		
			
				|  |  | -      this.velocity.set(0, 0, 0);
 | 
	
		
			
				|  |  | -      this.el.setAttribute('velocity', this.velocity);
 | 
	
		
			
				|  |  | -    } else {
 | 
	
		
			
				|  |  | -      this.updateVelocity(dt);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +  Copyright © 2010-2014 three.js authors
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /*******************************************************************
 | 
	
		
			
				|  |  | -   * Rotation
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | +  Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
	
		
			
				|  |  | +  of this software and associated documentation files (the "Software"), to deal
 | 
	
		
			
				|  |  | +  in the Software without restriction, including without limitation the rights
 | 
	
		
			
				|  |  | +  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
	
		
			
				|  |  | +  copies of the Software, and to permit persons to whom the Software is
 | 
	
		
			
				|  |  | +  furnished to do so, subject to the following conditions:
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  updateRotation: function (dt) {
 | 
	
		
			
				|  |  | -    var control, dRotation,
 | 
	
		
			
				|  |  | -        data = this.data;
 | 
	
		
			
				|  |  | +  The above copyright notice and this permission notice shall be included in
 | 
	
		
			
				|  |  | +  all copies or substantial portions of the Software.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    for (var i = 0, l = data.rotationControls.length; i < l; i++) {
 | 
	
		
			
				|  |  | -      control = this.el.components[data.rotationControls[i] + COMPONENT_SUFFIX];
 | 
	
		
			
				|  |  | -      if (control && control.isRotationActive()) {
 | 
	
		
			
				|  |  | -        if (control.getRotationDelta) {
 | 
	
		
			
				|  |  | -          dRotation = control.getRotationDelta(dt);
 | 
	
		
			
				|  |  | -          dRotation.multiplyScalar(data.rotationSensitivity);
 | 
	
		
			
				|  |  | -          this.yaw.rotation.y -= dRotation.x;
 | 
	
		
			
				|  |  | -          this.pitch.rotation.x -= dRotation.y;
 | 
	
		
			
				|  |  | -          this.pitch.rotation.x = Math.max(-PI_2, Math.min(PI_2, this.pitch.rotation.x));
 | 
	
		
			
				|  |  | -          this.el.setAttribute('rotation', {
 | 
	
		
			
				|  |  | -            x: THREE.Math.radToDeg(this.pitch.rotation.x),
 | 
	
		
			
				|  |  | -            y: THREE.Math.radToDeg(this.yaw.rotation.y),
 | 
	
		
			
				|  |  | -            z: 0
 | 
	
		
			
				|  |  | -          });
 | 
	
		
			
				|  |  | -        } else if (control.getRotation) {
 | 
	
		
			
				|  |  | -          this.el.setAttribute('rotation', control.getRotation());
 | 
	
		
			
				|  |  | -        } else {
 | 
	
		
			
				|  |  | -          throw new Error('Incompatible rotation controls: %s', data.rotationControls[i]);
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        break;
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
	
		
			
				|  |  | +  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
	
		
			
				|  |  | +  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
	
		
			
				|  |  | +  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
	
		
			
				|  |  | +  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
	
		
			
				|  |  | +  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /*******************************************************************
 | 
	
		
			
				|  |  | -   * Movement
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | +  THE SOFTWARE.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  updateVelocity: function (dt) {
 | 
	
		
			
				|  |  | -    var control, dVelocity,
 | 
	
		
			
				|  |  | -        velocity = this.velocity,
 | 
	
		
			
				|  |  | -        data = this.data;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (data.movementEnabled) {
 | 
	
		
			
				|  |  | -      for (var i = 0, l = data.movementControls.length; i < l; i++) {
 | 
	
		
			
				|  |  | -        control = this.el.components[data.movementControls[i] + COMPONENT_SUFFIX];
 | 
	
		
			
				|  |  | -        if (control && control.isVelocityActive()) {
 | 
	
		
			
				|  |  | -          if (control.getVelocityDelta) {
 | 
	
		
			
				|  |  | -            dVelocity = control.getVelocityDelta(dt);
 | 
	
		
			
				|  |  | -          } else if (control.getVelocity) {
 | 
	
		
			
				|  |  | -            this.el.setAttribute('velocity', control.getVelocity());
 | 
	
		
			
				|  |  | -            return;
 | 
	
		
			
				|  |  | -          } else if (control.getPositionDelta) {
 | 
	
		
			
				|  |  | -            velocity.copy(control.getPositionDelta(dt).multiplyScalar(1000 / dt));
 | 
	
		
			
				|  |  | -            this.el.setAttribute('velocity', velocity);
 | 
	
		
			
				|  |  | -            return;
 | 
	
		
			
				|  |  | -          } else {
 | 
	
		
			
				|  |  | -            throw new Error('Incompatible movement controls: ', data.movementControls[i]);
 | 
	
		
			
				|  |  | -          }
 | 
	
		
			
				|  |  | -          break;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +    @author mark lundin / http://mark-lundin.com
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    velocity.copy(this.el.getAttribute('velocity'));
 | 
	
		
			
				|  |  | -    velocity.x -= velocity.x * data.movementEasing * dt / 1000;
 | 
	
		
			
				|  |  | -    velocity.y -= velocity.y * data.movementEasingY * dt / 1000;
 | 
	
		
			
				|  |  | -    velocity.z -= velocity.z * data.movementEasing * dt / 1000;
 | 
	
		
			
				|  |  | +    This is a 3D implementation of the Quick Hull algorithm.
 | 
	
		
			
				|  |  | +    It is a fast way of computing a convex hull with average complexity
 | 
	
		
			
				|  |  | +    of O(n log(n)).
 | 
	
		
			
				|  |  | +    It uses depends on three.js and is supposed to create THREE.Geometry.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (dVelocity && data.movementEnabled) {
 | 
	
		
			
				|  |  | -      // Set acceleration
 | 
	
		
			
				|  |  | -      if (dVelocity.length() > 1) {
 | 
	
		
			
				|  |  | -        dVelocity.setLength(this.data.movementAcceleration * dt / 1000);
 | 
	
		
			
				|  |  | -      } else {
 | 
	
		
			
				|  |  | -        dVelocity.multiplyScalar(this.data.movementAcceleration * dt / 1000);
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | +    It's also very messy
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      // Rotate to heading
 | 
	
		
			
				|  |  | -      var rotation = this.el.getAttribute('rotation');
 | 
	
		
			
				|  |  | -      if (rotation) {
 | 
	
		
			
				|  |  | -        this.heading.set(
 | 
	
		
			
				|  |  | -          data.fly ? THREE.Math.degToRad(rotation.x) : 0,
 | 
	
		
			
				|  |  | -          THREE.Math.degToRad(rotation.y),
 | 
	
		
			
				|  |  | -          0
 | 
	
		
			
				|  |  | -        );
 | 
	
		
			
				|  |  | -        dVelocity.applyEuler(this.heading);
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +module.exports = (function(){
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  var faces     = [],
 | 
	
		
			
				|  |  | +    faceStack   = [],
 | 
	
		
			
				|  |  | +    i, NUM_POINTS, extremes,
 | 
	
		
			
				|  |  | +    max     = 0,
 | 
	
		
			
				|  |  | +    dcur, current, j, v0, v1, v2, v3,
 | 
	
		
			
				|  |  | +    N, D;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      velocity.add(dVelocity);
 | 
	
		
			
				|  |  | +  var ab, ac, ax,
 | 
	
		
			
				|  |  | +    suba, subb, normal,
 | 
	
		
			
				|  |  | +    diff, subaA, subaB, subC;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      // TODO - Several issues here:
 | 
	
		
			
				|  |  | -      // (1) Interferes w/ gravity.
 | 
	
		
			
				|  |  | -      // (2) Interferes w/ jumping.
 | 
	
		
			
				|  |  | -      // (3) Likely to interfere w/ relative position to moving platform.
 | 
	
		
			
				|  |  | -      // if (velocity.length() > data.movementSpeed) {
 | 
	
		
			
				|  |  | -      //   velocity.setLength(data.movementSpeed);
 | 
	
		
			
				|  |  | -      // }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +  function reset(){
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    ab    = new THREE.Vector3(),
 | 
	
		
			
				|  |  | +    ac    = new THREE.Vector3(),
 | 
	
		
			
				|  |  | +    ax    = new THREE.Vector3(),
 | 
	
		
			
				|  |  | +    suba  = new THREE.Vector3(),
 | 
	
		
			
				|  |  | +    subb  = new THREE.Vector3(),
 | 
	
		
			
				|  |  | +    normal  = new THREE.Vector3(),
 | 
	
		
			
				|  |  | +    diff  = new THREE.Vector3(),
 | 
	
		
			
				|  |  | +    subaA = new THREE.Vector3(),
 | 
	
		
			
				|  |  | +    subaB = new THREE.Vector3(),
 | 
	
		
			
				|  |  | +    subC  = new THREE.Vector3();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    this.el.setAttribute('velocity', velocity);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{}],89:[function(require,module,exports){
 | 
	
		
			
				|  |  | -var LoopMode = {
 | 
	
		
			
				|  |  | -  once: THREE.LoopOnce,
 | 
	
		
			
				|  |  | -  repeat: THREE.LoopRepeat,
 | 
	
		
			
				|  |  | -  pingpong: THREE.LoopPingPong
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | +  //temporary vectors
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * animation-mixer
 | 
	
		
			
				|  |  | - *
 | 
	
		
			
				|  |  | - * Player for animation clips. Intended to be compatible with any model format that supports
 | 
	
		
			
				|  |  | - * skeletal or morph animations through THREE.AnimationMixer.
 | 
	
		
			
				|  |  | - * See: https://threejs.org/docs/?q=animation#Reference/Animation/AnimationMixer
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -module.exports = {
 | 
	
		
			
				|  |  | -  schema: {
 | 
	
		
			
				|  |  | -    clip:  {default: '*'},
 | 
	
		
			
				|  |  | -    duration: {default: 0},
 | 
	
		
			
				|  |  | -    crossFadeDuration: {default: 0},
 | 
	
		
			
				|  |  | -    loop: {default: 'repeat', oneOf: Object.keys(LoopMode)},
 | 
	
		
			
				|  |  | -    repetitions: {default: Infinity, min: 0}
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +  function process( points ){
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  init: function () {
 | 
	
		
			
				|  |  | -    /** @type {THREE.Mesh} */
 | 
	
		
			
				|  |  | -    this.model = null;
 | 
	
		
			
				|  |  | -    /** @type {THREE.AnimationMixer} */
 | 
	
		
			
				|  |  | -    this.mixer = null;
 | 
	
		
			
				|  |  | -    /** @type {Array<THREE.AnimationAction>} */
 | 
	
		
			
				|  |  | -    this.activeActions = [];
 | 
	
		
			
				|  |  | +    // Iterate through all the faces and remove
 | 
	
		
			
				|  |  | +    while( faceStack.length > 0  ){
 | 
	
		
			
				|  |  | +      cull( faceStack.shift(), points );
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    var model = this.el.getObject3D('mesh');
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (model) {
 | 
	
		
			
				|  |  | -      this.load(model);
 | 
	
		
			
				|  |  | -    } else {
 | 
	
		
			
				|  |  | -      this.el.addEventListener('model-loaded', function(e) {
 | 
	
		
			
				|  |  | -        this.load(e.detail.model);
 | 
	
		
			
				|  |  | -      }.bind(this));
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +  var norm = function(){
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  load: function (model) {
 | 
	
		
			
				|  |  | -    var el = this.el;
 | 
	
		
			
				|  |  | -    this.model = model;
 | 
	
		
			
				|  |  | -    this.mixer = new THREE.AnimationMixer(model);
 | 
	
		
			
				|  |  | -    this.mixer.addEventListener('loop', function (e) {
 | 
	
		
			
				|  |  | -      el.emit('animation-loop', {action: e.action, loopDelta: e.loopDelta});
 | 
	
		
			
				|  |  | -    }.bind(this));
 | 
	
		
			
				|  |  | -    this.mixer.addEventListener('finished', function (e) {
 | 
	
		
			
				|  |  | -      el.emit('animation-finished', {action: e.action, direction: e.direction});
 | 
	
		
			
				|  |  | -    }.bind(this));
 | 
	
		
			
				|  |  | -    if (this.data.clip) this.update({});
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +    var ca = new THREE.Vector3(),
 | 
	
		
			
				|  |  | +      ba = new THREE.Vector3(),
 | 
	
		
			
				|  |  | +      N = new THREE.Vector3();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  remove: function () {
 | 
	
		
			
				|  |  | -    if (this.mixer) this.mixer.stopAllAction();
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +    return function( a, b, c ){
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  update: function (previousData) {
 | 
	
		
			
				|  |  | -    if (!previousData) return;
 | 
	
		
			
				|  |  | +      ca.subVectors( c, a );
 | 
	
		
			
				|  |  | +      ba.subVectors( b, a );
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    this.stopAction();
 | 
	
		
			
				|  |  | +      N.crossVectors( ca, ba );
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (this.data.clip) {
 | 
	
		
			
				|  |  | -      this.playAction();
 | 
	
		
			
				|  |  | +      return N.normalize();
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  stopAction: function () {
 | 
	
		
			
				|  |  | -    var data = this.data;
 | 
	
		
			
				|  |  | -    for (var i = 0; i < this.activeActions.length; i++) {
 | 
	
		
			
				|  |  | -      data.crossFadeDuration
 | 
	
		
			
				|  |  | -        ? this.activeActions[i].fadeOut(data.crossFadeDuration)
 | 
	
		
			
				|  |  | -        : this.activeActions[i].stop();
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    this.activeActions.length = 0;
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +  }();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  playAction: function () {
 | 
	
		
			
				|  |  | -    if (!this.mixer) return;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    var model = this.model,
 | 
	
		
			
				|  |  | -        data = this.data,
 | 
	
		
			
				|  |  | -        clips = model.animations || (model.geometry || {}).animations || [];
 | 
	
		
			
				|  |  | +  function getNormal( face, points ){
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (!clips.length) return;
 | 
	
		
			
				|  |  | +    if( face.normal !== undefined ) return face.normal;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    var re = wildcardToRegExp(data.clip);
 | 
	
		
			
				|  |  | +    var p0 = points[face[0]],
 | 
	
		
			
				|  |  | +      p1 = points[face[1]],
 | 
	
		
			
				|  |  | +      p2 = points[face[2]];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    for (var clip, i = 0; (clip = clips[i]); i++) {
 | 
	
		
			
				|  |  | -      if (clip.name.match(re)) {
 | 
	
		
			
				|  |  | -        var action = this.mixer.clipAction(clip, model);
 | 
	
		
			
				|  |  | -        action.enabled = true;
 | 
	
		
			
				|  |  | -        if (data.duration) action.setDuration(data.duration);
 | 
	
		
			
				|  |  | -        action
 | 
	
		
			
				|  |  | -          .setLoop(LoopMode[data.loop], data.repetitions)
 | 
	
		
			
				|  |  | -          .fadeIn(data.crossFadeDuration)
 | 
	
		
			
				|  |  | -          .play();
 | 
	
		
			
				|  |  | -        this.activeActions.push(action);
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +    ab.subVectors( p1, p0 );
 | 
	
		
			
				|  |  | +    ac.subVectors( p2, p0 );
 | 
	
		
			
				|  |  | +    normal.crossVectors( ac, ab );
 | 
	
		
			
				|  |  | +    normal.normalize();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    return face.normal = normal.clone();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  tick: function (t, dt) {
 | 
	
		
			
				|  |  | -    if (this.mixer && !isNaN(dt)) this.mixer.update(dt / 1000);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * Creates a RegExp from the given string, converting asterisks to .* expressions,
 | 
	
		
			
				|  |  | - * and escaping all other characters.
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -function wildcardToRegExp (s) {
 | 
	
		
			
				|  |  | -  return new RegExp('^' + s.split(/\*+/).map(regExpEscape).join('.*') + '$');
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * RegExp-escapes all characters in the given string.
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -function regExpEscape (s) {
 | 
	
		
			
				|  |  | -  return s.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&');
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | +  function assignPoints( face, pointset, points ){
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{}],90:[function(require,module,exports){
 | 
	
		
			
				|  |  | -THREE.FBXLoader = require('../../lib/FBXLoader');
 | 
	
		
			
				|  |  | +    // ASSIGNING POINTS TO FACE
 | 
	
		
			
				|  |  | +    var p0 = points[face[0]],
 | 
	
		
			
				|  |  | +      dots = [], apex,
 | 
	
		
			
				|  |  | +      norm = getNormal( face, points );
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * fbx-model
 | 
	
		
			
				|  |  | - *
 | 
	
		
			
				|  |  | - * Loader for FBX format. Supports ASCII, but *not* binary, models.
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -module.exports = {
 | 
	
		
			
				|  |  | -  schema: {
 | 
	
		
			
				|  |  | -    src:         { type: 'asset' },
 | 
	
		
			
				|  |  | -    crossorigin: { default: '' }
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  init: function () {
 | 
	
		
			
				|  |  | -    this.model = null;
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +    // Sory all the points by there distance from the plane
 | 
	
		
			
				|  |  | +    pointset.sort( function( aItem, bItem ){
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  update: function () {
 | 
	
		
			
				|  |  | -    var loader,
 | 
	
		
			
				|  |  | -        data = this.data;
 | 
	
		
			
				|  |  | -    if (!data.src) return;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    this.remove();
 | 
	
		
			
				|  |  | -    loader = new THREE.FBXLoader();
 | 
	
		
			
				|  |  | -    if (data.crossorigin) loader.setCrossOrigin(data.crossorigin);
 | 
	
		
			
				|  |  | -    loader.load(data.src, this.load.bind(this));
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +      dots[aItem.x/3] = dots[aItem.x/3] !== undefined ? dots[aItem.x/3] : norm.dot( suba.subVectors( aItem, p0 ));
 | 
	
		
			
				|  |  | +      dots[bItem.x/3] = dots[bItem.x/3] !== undefined ? dots[bItem.x/3] : norm.dot( subb.subVectors( bItem, p0 ));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  load: function (model) {
 | 
	
		
			
				|  |  | -    this.model = model;
 | 
	
		
			
				|  |  | -    this.el.setObject3D('mesh', model);
 | 
	
		
			
				|  |  | -    this.el.emit('model-loaded', {format: 'fbx', model: model});
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +      return dots[aItem.x/3] - dots[bItem.x/3] ;
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  remove: function () {
 | 
	
		
			
				|  |  | -    if (this.model) this.el.removeObject3D('mesh');
 | 
	
		
			
				|  |  | +    //TODO :: Must be a faster way of finding and index in this array
 | 
	
		
			
				|  |  | +    var index = pointset.length;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if( index === 1 ) dots[pointset[0].x/3] = norm.dot( suba.subVectors( pointset[0], p0 ));
 | 
	
		
			
				|  |  | +    while( index-- > 0 && dots[pointset[index].x/3] > 0 )
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    var point;
 | 
	
		
			
				|  |  | +    if( index + 1 < pointset.length && dots[pointset[index+1].x/3] > 0 ){
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      face.visiblePoints  = pointset.splice( index + 1 );
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{"../../lib/FBXLoader":3}],91:[function(require,module,exports){
 | 
	
		
			
				|  |  | -var fetchScript = require('../../lib/fetch-script')();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -var LOADER_SRC = 'https://rawgit.com/mrdoob/three.js/r86/examples/js/loaders/GLTFLoader.js';
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * Legacy loader for glTF 1.0 models.
 | 
	
		
			
				|  |  | - * Asynchronously loads THREE.GLTFLoader from rawgit.
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -module.exports.Component = {
 | 
	
		
			
				|  |  | -  schema: {type: 'model'},
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  init: function () {
 | 
	
		
			
				|  |  | -    this.model = null;
 | 
	
		
			
				|  |  | -    this.loader = null;
 | 
	
		
			
				|  |  | -    this.loaderPromise = loadLoader().then(function () {
 | 
	
		
			
				|  |  | -      this.loader = new THREE.GLTFLoader();
 | 
	
		
			
				|  |  | -      this.loader.setCrossOrigin('Anonymous');
 | 
	
		
			
				|  |  | -    }.bind(this));
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +  function cull( face, points ){
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    var i = faces.length,
 | 
	
		
			
				|  |  | +      dot, visibleFace, currentFace,
 | 
	
		
			
				|  |  | +      visibleFaces = [face];
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    var apex = points.indexOf( face.visiblePoints.pop() );
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // Iterate through all other faces...
 | 
	
		
			
				|  |  | +    while( i-- > 0 ){
 | 
	
		
			
				|  |  | +      currentFace = faces[i];
 | 
	
		
			
				|  |  | +      if( currentFace !== face ){
 | 
	
		
			
				|  |  | +        // ...and check if they're pointing in the same direction
 | 
	
		
			
				|  |  | +        dot = getNormal( currentFace, points ).dot( diff.subVectors( points[apex], points[currentFace[0]] ));
 | 
	
		
			
				|  |  | +        if( dot > 0 ){
 | 
	
		
			
				|  |  | +          visibleFaces.push( currentFace );
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    var index, neighbouringIndex, vertex;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // Determine Perimeter - Creates a bounded horizon
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // 1. Pick an edge A out of all possible edges
 | 
	
		
			
				|  |  | +    // 2. Check if A is shared by any other face. a->b === b->a
 | 
	
		
			
				|  |  | +      // 2.1 for each edge in each triangle, isShared = ( f1.a == f2.a && f1.b == f2.b ) || ( f1.a == f2.b && f1.b == f2.a )
 | 
	
		
			
				|  |  | +    // 3. If not shared, then add to convex horizon set,
 | 
	
		
			
				|  |  | +        //pick an end point (N) of the current edge A and choose a new edge NA connected to A.
 | 
	
		
			
				|  |  | +        //Restart from 1.
 | 
	
		
			
				|  |  | +    // 4. If A is shared, it is not an horizon edge, therefore flag both faces that share this edge as candidates for culling
 | 
	
		
			
				|  |  | +    // 5. If candidate geometry is a degenrate triangle (ie. the tangent space normal cannot be computed) then remove that triangle from all further processing
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  update: function () {
 | 
	
		
			
				|  |  | -    var self = this;
 | 
	
		
			
				|  |  | -    var el = this.el;
 | 
	
		
			
				|  |  | -    var src = this.data;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (!src) { return; }
 | 
	
		
			
				|  |  | +    var j = i = visibleFaces.length;
 | 
	
		
			
				|  |  | +    var isDistinct = false,
 | 
	
		
			
				|  |  | +      hasOneVisibleFace = i === 1,
 | 
	
		
			
				|  |  | +      cull = [],
 | 
	
		
			
				|  |  | +      perimeter = [],
 | 
	
		
			
				|  |  | +      edgeIndex = 0, compareFace, nextIndex,
 | 
	
		
			
				|  |  | +      a, b;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    this.remove();
 | 
	
		
			
				|  |  | +    var allPoints = [];
 | 
	
		
			
				|  |  | +    var originFace = [visibleFaces[0][0], visibleFaces[0][1], visibleFaces[0][1], visibleFaces[0][2], visibleFaces[0][2], visibleFaces[0][0]];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    this.loaderPromise.then(function () {
 | 
	
		
			
				|  |  | -      this.loader.load(src, function gltfLoaded (gltfModel) {
 | 
	
		
			
				|  |  | -        self.model = gltfModel.scene;
 | 
	
		
			
				|  |  | -        self.model.animations = gltfModel.animations;
 | 
	
		
			
				|  |  | -        self.system.registerModel(self.model);
 | 
	
		
			
				|  |  | -        el.setObject3D('mesh', self.model);
 | 
	
		
			
				|  |  | -        el.emit('model-loaded', {format: 'gltf', model: self.model});
 | 
	
		
			
				|  |  | -      });
 | 
	
		
			
				|  |  | -    }.bind(this));
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  remove: function () {
 | 
	
		
			
				|  |  | -    if (!this.model) { return; }
 | 
	
		
			
				|  |  | -    this.el.removeObject3D('mesh');
 | 
	
		
			
				|  |  | -    this.system.unregisterModel(this.model);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | +    if( visibleFaces.length === 1 ){
 | 
	
		
			
				|  |  | +      currentFace = visibleFaces[0];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * glTF model system.
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -module.exports.System = {
 | 
	
		
			
				|  |  | -  init: function () {
 | 
	
		
			
				|  |  | -    this.models = [];
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +      perimeter = [currentFace[0], currentFace[1], currentFace[1], currentFace[2], currentFace[2], currentFace[0]];
 | 
	
		
			
				|  |  | +      // remove visible face from list of faces
 | 
	
		
			
				|  |  | +      if( faceStack.indexOf( currentFace ) > -1 ){
 | 
	
		
			
				|  |  | +        faceStack.splice( faceStack.indexOf( currentFace ), 1 );
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /**
 | 
	
		
			
				|  |  | -   * Updates shaders for all glTF models in the system.
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | -  tick: function () {
 | 
	
		
			
				|  |  | -    var sceneEl = this.sceneEl;
 | 
	
		
			
				|  |  | -    if (sceneEl.hasLoaded && this.models.length) {
 | 
	
		
			
				|  |  | -      THREE.GLTFLoader.Shaders.update(sceneEl.object3D, sceneEl.camera);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /**
 | 
	
		
			
				|  |  | -   * Registers a glTF asset.
 | 
	
		
			
				|  |  | -   * @param {object} gltf Asset containing a scene and (optional) animations and cameras.
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | -  registerModel: function (gltf) {
 | 
	
		
			
				|  |  | -    this.models.push(gltf);
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +      if( currentFace.visiblePoints ) allPoints = allPoints.concat( currentFace.visiblePoints );
 | 
	
		
			
				|  |  | +      faces.splice( faces.indexOf( currentFace ), 1 );
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /**
 | 
	
		
			
				|  |  | -   * Unregisters a glTF asset.
 | 
	
		
			
				|  |  | -   * @param  {object} gltf Asset containing a scene and (optional) animations and cameras.
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | -  unregisterModel: function (gltf) {
 | 
	
		
			
				|  |  | -    var models = this.models;
 | 
	
		
			
				|  |  | -    var index = models.indexOf(gltf);
 | 
	
		
			
				|  |  | -    if (index >= 0) {
 | 
	
		
			
				|  |  | -      models.splice(index, 1);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | +    }else{
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -var loadLoader = (function () {
 | 
	
		
			
				|  |  | -  var promise;
 | 
	
		
			
				|  |  | -  return function () {
 | 
	
		
			
				|  |  | -    promise = promise || fetchScript(LOADER_SRC);
 | 
	
		
			
				|  |  | -    return promise;
 | 
	
		
			
				|  |  | -  };
 | 
	
		
			
				|  |  | -}());
 | 
	
		
			
				|  |  | +      while( i-- > 0  ){  // for each visible face
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{"../../lib/fetch-script":8}],92:[function(require,module,exports){
 | 
	
		
			
				|  |  | -var fetchScript = require('../../lib/fetch-script')();
 | 
	
		
			
				|  |  | +        currentFace = visibleFaces[i];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -var LOADER_SRC = 'https://rawgit.com/mrdoob/three.js/r87/examples/js/loaders/GLTFLoader.js';
 | 
	
		
			
				|  |  | -// Monkeypatch while waiting for three.js r86.
 | 
	
		
			
				|  |  | -if (THREE.PropertyBinding.sanitizeNodeName === undefined) {
 | 
	
		
			
				|  |  | +        // remove visible face from list of faces
 | 
	
		
			
				|  |  | +        if( faceStack.indexOf( currentFace ) > -1 ){
 | 
	
		
			
				|  |  | +          faceStack.splice( faceStack.indexOf( currentFace ), 1 );
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  THREE.PropertyBinding.sanitizeNodeName = function (s) {
 | 
	
		
			
				|  |  | -    return s.replace( /\s/g, '_' ).replace( /[^\w-]/g, '' );
 | 
	
		
			
				|  |  | -  };
 | 
	
		
			
				|  |  | +        if( currentFace.visiblePoints ) allPoints = allPoints.concat( currentFace.visiblePoints );
 | 
	
		
			
				|  |  | +        faces.splice( faces.indexOf( currentFace ), 1 );
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * Upcoming loader for glTF 2.0 models.
 | 
	
		
			
				|  |  | - * Asynchronously loads THREE.GLTF2Loader from rawgit.
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -module.exports = {
 | 
	
		
			
				|  |  | -  schema: {type: 'model'},
 | 
	
		
			
				|  |  | +        var isSharedEdge;
 | 
	
		
			
				|  |  | +        cEdgeIndex = 0;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  init: function () {
 | 
	
		
			
				|  |  | -    this.model = null;
 | 
	
		
			
				|  |  | -    this.loader = null;
 | 
	
		
			
				|  |  | -    this.loaderPromise = loadLoader().then(function () {
 | 
	
		
			
				|  |  | -      this.loader = new THREE.GLTFLoader();
 | 
	
		
			
				|  |  | -      this.loader.setCrossOrigin('Anonymous');
 | 
	
		
			
				|  |  | -    }.bind(this));
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +        while( cEdgeIndex < 3 ){ // Iterate through it's edges
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  update: function () {
 | 
	
		
			
				|  |  | -    var self = this;
 | 
	
		
			
				|  |  | -    var el = this.el;
 | 
	
		
			
				|  |  | -    var src = this.data;
 | 
	
		
			
				|  |  | +          isSharedEdge = false;
 | 
	
		
			
				|  |  | +          j = visibleFaces.length;
 | 
	
		
			
				|  |  | +          a = currentFace[cEdgeIndex]
 | 
	
		
			
				|  |  | +          b = currentFace[(cEdgeIndex+1)%3];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (!src) { return; }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    this.remove();
 | 
	
		
			
				|  |  | +          while( j-- > 0 && !isSharedEdge ){ // find another visible faces
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    this.loaderPromise.then(function () {
 | 
	
		
			
				|  |  | -      this.loader.load(src, function gltfLoaded (gltfModel) {
 | 
	
		
			
				|  |  | -        self.model = gltfModel.scene;
 | 
	
		
			
				|  |  | -        self.model.animations = gltfModel.animations;
 | 
	
		
			
				|  |  | -        el.setObject3D('mesh', self.model);
 | 
	
		
			
				|  |  | -        el.emit('model-loaded', {format: 'gltf', model: self.model});
 | 
	
		
			
				|  |  | -      });
 | 
	
		
			
				|  |  | -    }.bind(this));
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +            compareFace = visibleFaces[j];
 | 
	
		
			
				|  |  | +            edgeIndex = 0;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  remove: function () {
 | 
	
		
			
				|  |  | -    if (!this.model) { return; }
 | 
	
		
			
				|  |  | -    this.el.removeObject3D('mesh');
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | +            // isSharedEdge = compareFace == currentFace;
 | 
	
		
			
				|  |  | +            if( compareFace !== currentFace ){
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -var loadLoader = (function () {
 | 
	
		
			
				|  |  | -  var promise;
 | 
	
		
			
				|  |  | -  return function () {
 | 
	
		
			
				|  |  | -    promise = promise || fetchScript(LOADER_SRC);
 | 
	
		
			
				|  |  | -    return promise;
 | 
	
		
			
				|  |  | -  };
 | 
	
		
			
				|  |  | -}());
 | 
	
		
			
				|  |  | +              while( edgeIndex < 3 && !isSharedEdge ){ //Check all it's indices
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{"../../lib/fetch-script":8}],93:[function(require,module,exports){
 | 
	
		
			
				|  |  | -module.exports = {
 | 
	
		
			
				|  |  | -  'animation-mixer': require('./animation-mixer'),
 | 
	
		
			
				|  |  | -  'fbx-model': require('./fbx-model'),
 | 
	
		
			
				|  |  | -  'gltf-model-next': require('./gltf-model-next'),
 | 
	
		
			
				|  |  | -  'gltf-model-legacy': require('./gltf-model-legacy'),
 | 
	
		
			
				|  |  | -  'json-model': require('./json-model'),
 | 
	
		
			
				|  |  | -  'object-model': require('./object-model'),
 | 
	
		
			
				|  |  | -  'ply-model': require('./ply-model'),
 | 
	
		
			
				|  |  | -  'three-model': require('./three-model'),
 | 
	
		
			
				|  |  | +                nextIndex = ( edgeIndex + 1 );
 | 
	
		
			
				|  |  | +                isSharedEdge = ( compareFace[edgeIndex] === a && compareFace[nextIndex%3] === b ) ||
 | 
	
		
			
				|  |  | +                         ( compareFace[edgeIndex] === b && compareFace[nextIndex%3] === a );
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  registerAll: function (AFRAME) {
 | 
	
		
			
				|  |  | -    if (this._registered) return;
 | 
	
		
			
				|  |  | +                edgeIndex++;
 | 
	
		
			
				|  |  | +              }
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    AFRAME = AFRAME || window.AFRAME;
 | 
	
		
			
				|  |  | +          if( !isSharedEdge || hasOneVisibleFace ){
 | 
	
		
			
				|  |  | +            perimeter.push( a );
 | 
	
		
			
				|  |  | +            perimeter.push( b );
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // THREE.AnimationMixer
 | 
	
		
			
				|  |  | -    if (!AFRAME.components['animation-mixer']) {
 | 
	
		
			
				|  |  | -      AFRAME.registerComponent('animation-mixer', this['animation-mixer']);
 | 
	
		
			
				|  |  | +          cEdgeIndex++;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // THREE.PlyLoader
 | 
	
		
			
				|  |  | -    if (!AFRAME.systems['ply-model']) {
 | 
	
		
			
				|  |  | -      AFRAME.registerSystem('ply-model', this['ply-model'].System);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    if (!AFRAME.components['ply-model']) {
 | 
	
		
			
				|  |  | -      AFRAME.registerComponent('ply-model', this['ply-model'].Component);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +    // create new face for all pairs around edge
 | 
	
		
			
				|  |  | +    i = 0;
 | 
	
		
			
				|  |  | +    var l = perimeter.length/2;
 | 
	
		
			
				|  |  | +    var f;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // THREE.FBXLoader
 | 
	
		
			
				|  |  | -    if (!AFRAME.components['fbx-model']) {
 | 
	
		
			
				|  |  | -      AFRAME.registerComponent('fbx-model', this['fbx-model']);
 | 
	
		
			
				|  |  | +    while( i < l ){
 | 
	
		
			
				|  |  | +      f = [ perimeter[i*2+1], apex, perimeter[i*2] ];
 | 
	
		
			
				|  |  | +      assignPoints( f, allPoints, points );
 | 
	
		
			
				|  |  | +      faces.push( f )
 | 
	
		
			
				|  |  | +      if( f.visiblePoints !== undefined  )faceStack.push( f );
 | 
	
		
			
				|  |  | +      i++;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // THREE.GLTF2Loader
 | 
	
		
			
				|  |  | -    if (!AFRAME.components['gltf-model-next']) {
 | 
	
		
			
				|  |  | -      AFRAME.registerComponent('gltf-model-next', this['gltf-model-next']);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // THREE.GLTFLoader
 | 
	
		
			
				|  |  | -    if (!AFRAME.components['gltf-model-legacy']) {
 | 
	
		
			
				|  |  | -      AFRAME.registerComponent('gltf-model-legacy', this['gltf-model-legacy'].Component);
 | 
	
		
			
				|  |  | -      AFRAME.registerSystem('gltf-model-legacy', this['gltf-model-legacy'].System);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +  var distSqPointSegment = function(){
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // THREE.JsonLoader
 | 
	
		
			
				|  |  | -    if (!AFRAME.components['json-model']) {
 | 
	
		
			
				|  |  | -      AFRAME.registerComponent('json-model', this['json-model']);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +    var ab = new THREE.Vector3(),
 | 
	
		
			
				|  |  | +      ac = new THREE.Vector3(),
 | 
	
		
			
				|  |  | +      bc = new THREE.Vector3();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // THREE.ObjectLoader
 | 
	
		
			
				|  |  | -    if (!AFRAME.components['object-model']) {
 | 
	
		
			
				|  |  | -      AFRAME.registerComponent('object-model', this['object-model']);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +    return function( a, b, c ){
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // (deprecated) THREE.JsonLoader and THREE.ObjectLoader
 | 
	
		
			
				|  |  | -    if (!AFRAME.components['three-model']) {
 | 
	
		
			
				|  |  | -      AFRAME.registerComponent('three-model', this['three-model']);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +        ab.subVectors( b, a );
 | 
	
		
			
				|  |  | +        ac.subVectors( c, a );
 | 
	
		
			
				|  |  | +        bc.subVectors( c, b );
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    this._registered = true;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | +        var e = ac.dot(ab);
 | 
	
		
			
				|  |  | +        if (e < 0.0) return ac.dot( ac );
 | 
	
		
			
				|  |  | +        var f = ab.dot( ab );
 | 
	
		
			
				|  |  | +        if (e >= f) return bc.dot(  bc );
 | 
	
		
			
				|  |  | +        return ac.dot( ac ) - e * e / f;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{"./animation-mixer":89,"./fbx-model":90,"./gltf-model-legacy":91,"./gltf-model-next":92,"./json-model":94,"./object-model":95,"./ply-model":96,"./three-model":97}],94:[function(require,module,exports){
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * json-model
 | 
	
		
			
				|  |  | - *
 | 
	
		
			
				|  |  | - * Loader for THREE.js JSON format. Somewhat confusingly, there are two different THREE.js formats,
 | 
	
		
			
				|  |  | - * both having the .json extension. This loader supports only THREE.JsonLoader, which typically
 | 
	
		
			
				|  |  | - * includes only a single mesh.
 | 
	
		
			
				|  |  | - *
 | 
	
		
			
				|  |  | - * Check the console for errors, if in doubt. You may need to use `object-model` or
 | 
	
		
			
				|  |  | - * `blend-character-model` for some .js and .json files.
 | 
	
		
			
				|  |  | - *
 | 
	
		
			
				|  |  | - * See: https://clara.io/learn/user-guide/data_exchange/threejs_export
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -module.exports = {
 | 
	
		
			
				|  |  | -  schema: {
 | 
	
		
			
				|  |  | -    src:         { type: 'asset' },
 | 
	
		
			
				|  |  | -    crossorigin: { default: '' }
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  init: function () {
 | 
	
		
			
				|  |  | -    this.model = null;
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +  }();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  update: function () {
 | 
	
		
			
				|  |  | -    var loader,
 | 
	
		
			
				|  |  | -        data = this.data;
 | 
	
		
			
				|  |  | -    if (!data.src) return;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    this.remove();
 | 
	
		
			
				|  |  | -    loader = new THREE.JSONLoader();
 | 
	
		
			
				|  |  | -    if (data.crossorigin) loader.crossOrigin = data.crossorigin;
 | 
	
		
			
				|  |  | -    loader.load(data.src, function (geometry, materials) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      // Attempt to automatically detect common material options.
 | 
	
		
			
				|  |  | -      materials.forEach(function (mat) {
 | 
	
		
			
				|  |  | -        mat.vertexColors = (geometry.faces[0] || {}).color ? THREE.FaceColors : THREE.NoColors;
 | 
	
		
			
				|  |  | -        mat.skinning = !!(geometry.bones || []).length;
 | 
	
		
			
				|  |  | -        mat.morphTargets = !!(geometry.morphTargets || []).length;
 | 
	
		
			
				|  |  | -        mat.morphNormals = !!(geometry.morphNormals || []).length;
 | 
	
		
			
				|  |  | -      });
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      var model = (geometry.bones || []).length
 | 
	
		
			
				|  |  | -        ? new THREE.SkinnedMesh(geometry, new THREE.MultiMaterial(materials))
 | 
	
		
			
				|  |  | -        : new THREE.Mesh(geometry, new THREE.MultiMaterial(materials));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      this.load(model);
 | 
	
		
			
				|  |  | -    }.bind(this));
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +  return function( geometry ){
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    reset();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    points    = geometry.vertices;
 | 
	
		
			
				|  |  | +    faces     = [],
 | 
	
		
			
				|  |  | +    faceStack   = [],
 | 
	
		
			
				|  |  | +    i       = NUM_POINTS = points.length,
 | 
	
		
			
				|  |  | +    extremes  = points.slice( 0, 6 ),
 | 
	
		
			
				|  |  | +    max     = 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  load: function (model) {
 | 
	
		
			
				|  |  | -    this.model = model;
 | 
	
		
			
				|  |  | -    this.el.setObject3D('mesh', model);
 | 
	
		
			
				|  |  | -    this.el.emit('model-loaded', {format: 'json', model: model});
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +    /*
 | 
	
		
			
				|  |  | +     *  FIND EXTREMETIES
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    while( i-- > 0 ){
 | 
	
		
			
				|  |  | +      if( points[i].x < extremes[0].x ) extremes[0] = points[i];
 | 
	
		
			
				|  |  | +      if( points[i].x > extremes[1].x ) extremes[1] = points[i];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  remove: function () {
 | 
	
		
			
				|  |  | -    if (this.model) this.el.removeObject3D('mesh');
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | +      if( points[i].y < extremes[2].y ) extremes[2] = points[i];
 | 
	
		
			
				|  |  | +      if( points[i].y < extremes[3].y ) extremes[3] = points[i];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{}],95:[function(require,module,exports){
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * object-model
 | 
	
		
			
				|  |  | - *
 | 
	
		
			
				|  |  | - * Loader for THREE.js JSON format. Somewhat confusingly, there are two different THREE.js formats,
 | 
	
		
			
				|  |  | - * both having the .json extension. This loader supports only THREE.ObjectLoader, which typically
 | 
	
		
			
				|  |  | - * includes multiple meshes or an entire scene.
 | 
	
		
			
				|  |  | - *
 | 
	
		
			
				|  |  | - * Check the console for errors, if in doubt. You may need to use `json-model` or
 | 
	
		
			
				|  |  | - * `blend-character-model` for some .js and .json files.
 | 
	
		
			
				|  |  | - *
 | 
	
		
			
				|  |  | - * See: https://clara.io/learn/user-guide/data_exchange/threejs_export
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -module.exports = {
 | 
	
		
			
				|  |  | -  schema: {
 | 
	
		
			
				|  |  | -    src:         { type: 'asset' },
 | 
	
		
			
				|  |  | -    crossorigin: { default: '' }
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +      if( points[i].z < extremes[4].z ) extremes[4] = points[i];
 | 
	
		
			
				|  |  | +      if( points[i].z < extremes[5].z ) extremes[5] = points[i];
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  init: function () {
 | 
	
		
			
				|  |  | -    this.model = null;
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  update: function () {
 | 
	
		
			
				|  |  | -    var loader,
 | 
	
		
			
				|  |  | -        data = this.data;
 | 
	
		
			
				|  |  | -    if (!data.src) return;
 | 
	
		
			
				|  |  | +    /*
 | 
	
		
			
				|  |  | +     *  Find the longest line between the extremeties
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    this.remove();
 | 
	
		
			
				|  |  | -    loader = new THREE.ObjectLoader();
 | 
	
		
			
				|  |  | -    if (data.crossorigin) loader.setCrossOrigin(data.crossorigin);
 | 
	
		
			
				|  |  | -    loader.load(data.src, function(object) {
 | 
	
		
			
				|  |  | +    j = i = 6;
 | 
	
		
			
				|  |  | +    while( i-- > 0 ){
 | 
	
		
			
				|  |  | +      j = i - 1;
 | 
	
		
			
				|  |  | +      while( j-- > 0 ){
 | 
	
		
			
				|  |  | +          if( max < (dcur = extremes[i].distanceToSquared( extremes[j] )) ){
 | 
	
		
			
				|  |  | +        max = dcur;
 | 
	
		
			
				|  |  | +        v0 = extremes[ i ];
 | 
	
		
			
				|  |  | +        v1 = extremes[ j ];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      // Enable skinning, if applicable.
 | 
	
		
			
				|  |  | -      object.traverse(function(o) {
 | 
	
		
			
				|  |  | -        if (o instanceof THREE.SkinnedMesh && o.material) {
 | 
	
		
			
				|  |  | -          o.material.skinning = !!((o.geometry && o.geometry.bones) || []).length;
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | -      });
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      this.load(object);
 | 
	
		
			
				|  |  | -    }.bind(this));
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  load: function (model) {
 | 
	
		
			
				|  |  | -    this.model = model;
 | 
	
		
			
				|  |  | -    this.el.setObject3D('mesh', model);
 | 
	
		
			
				|  |  | -    this.el.emit('model-loaded', {format: 'json', model: model});
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +      // 3. Find the most distant point to the line segment, this creates a plane
 | 
	
		
			
				|  |  | +      i = 6;
 | 
	
		
			
				|  |  | +      max = 0;
 | 
	
		
			
				|  |  | +    while( i-- > 0 ){
 | 
	
		
			
				|  |  | +      dcur = distSqPointSegment( v0, v1, extremes[i]);
 | 
	
		
			
				|  |  | +      if( max < dcur ){
 | 
	
		
			
				|  |  | +        max = dcur;
 | 
	
		
			
				|  |  | +            v2 = extremes[ i ];
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  remove: function () {
 | 
	
		
			
				|  |  | -    if (this.model) this.el.removeObject3D('mesh');
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{}],96:[function(require,module,exports){
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * ply-model
 | 
	
		
			
				|  |  | - *
 | 
	
		
			
				|  |  | - * Wraps THREE.PLYLoader.
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -THREE.PLYLoader = require('../../lib/PLYLoader');
 | 
	
		
			
				|  |  | +      // 4. Find the most distant point to the plane.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * Loads, caches, resolves geometries.
 | 
	
		
			
				|  |  | - *
 | 
	
		
			
				|  |  | - * @member cache - Promises that resolve geometries keyed by `src`.
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -module.exports.System = {
 | 
	
		
			
				|  |  | -  init: function () {
 | 
	
		
			
				|  |  | -    this.cache = {};
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +      N = norm(v0, v1, v2);
 | 
	
		
			
				|  |  | +      D = N.dot( v0 );
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /**
 | 
	
		
			
				|  |  | -   * @returns {Promise}
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | -  getOrLoadGeometry: function (src, skipCache) {
 | 
	
		
			
				|  |  | -    var cache = this.cache;
 | 
	
		
			
				|  |  | -    var cacheItem = cache[src];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (!skipCache && cacheItem) {
 | 
	
		
			
				|  |  | -      return cacheItem;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +      max = 0;
 | 
	
		
			
				|  |  | +      i = NUM_POINTS;
 | 
	
		
			
				|  |  | +      while( i-- > 0 ){
 | 
	
		
			
				|  |  | +        dcur = Math.abs( points[i].dot( N ) - D );
 | 
	
		
			
				|  |  | +          if( max < dcur ){
 | 
	
		
			
				|  |  | +            max = dcur;
 | 
	
		
			
				|  |  | +            v3 = points[i];
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    cache[src] = new Promise(function (resolve) {
 | 
	
		
			
				|  |  | -      var loader = new THREE.PLYLoader();
 | 
	
		
			
				|  |  | -      loader.load(src, function (geometry) {
 | 
	
		
			
				|  |  | -        resolve(geometry);
 | 
	
		
			
				|  |  | -      });
 | 
	
		
			
				|  |  | -    });
 | 
	
		
			
				|  |  | -    return cache[src];
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -module.exports.Component = {
 | 
	
		
			
				|  |  | -  schema: {
 | 
	
		
			
				|  |  | -    skipCache: {type: 'boolean', default: false},
 | 
	
		
			
				|  |  | -    src: {type: 'asset'}
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  init: function () {
 | 
	
		
			
				|  |  | -    this.model = null;
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +      var v0Index = points.indexOf( v0 ),
 | 
	
		
			
				|  |  | +      v1Index = points.indexOf( v1 ),
 | 
	
		
			
				|  |  | +      v2Index = points.indexOf( v2 ),
 | 
	
		
			
				|  |  | +      v3Index = points.indexOf( v3 );
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  update: function () {
 | 
	
		
			
				|  |  | -    var data = this.data;
 | 
	
		
			
				|  |  | -    var el = this.el;
 | 
	
		
			
				|  |  | -    var loader;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (!data.src) {
 | 
	
		
			
				|  |  | -      console.warn('[%s] `src` property is required.', this.name);
 | 
	
		
			
				|  |  | -      return;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +    //  We now have a tetrahedron as the base geometry.
 | 
	
		
			
				|  |  | +    //  Now we must subdivide the
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // Get geometry from system, create and set mesh.
 | 
	
		
			
				|  |  | -    this.system.getOrLoadGeometry(data.src, data.skipCache).then(function (geometry) {
 | 
	
		
			
				|  |  | -      var model = createModel(geometry);
 | 
	
		
			
				|  |  | -      el.setObject3D('mesh', model);
 | 
	
		
			
				|  |  | -      el.emit('model-loaded', {format: 'ply', model: model});
 | 
	
		
			
				|  |  | -    });
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +      var tetrahedron =[
 | 
	
		
			
				|  |  | +        [ v2Index, v1Index, v0Index ],
 | 
	
		
			
				|  |  | +        [ v1Index, v3Index, v0Index ],
 | 
	
		
			
				|  |  | +        [ v2Index, v3Index, v1Index ],
 | 
	
		
			
				|  |  | +        [ v0Index, v3Index, v2Index ],
 | 
	
		
			
				|  |  | +    ];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  remove: function () {
 | 
	
		
			
				|  |  | -    if (this.model) { this.el.removeObject3D('mesh'); }
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -function createModel (geometry) {
 | 
	
		
			
				|  |  | -  return new THREE.Mesh(geometry, new THREE.MeshPhongMaterial({
 | 
	
		
			
				|  |  | -    color: 0xFFFFFF,
 | 
	
		
			
				|  |  | -    shading: THREE.FlatShading,
 | 
	
		
			
				|  |  | -    vertexColors: THREE.VertexColors,
 | 
	
		
			
				|  |  | -    shininess: 0
 | 
	
		
			
				|  |  | -  }));
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{"../../lib/PLYLoader":6}],97:[function(require,module,exports){
 | 
	
		
			
				|  |  | -var DEFAULT_ANIMATION = '__auto__';
 | 
	
		
			
				|  |  | +    subaA.subVectors( v1, v0 ).normalize();
 | 
	
		
			
				|  |  | +    subaB.subVectors( v2, v0 ).normalize();
 | 
	
		
			
				|  |  | +    subC.subVectors ( v3, v0 ).normalize();
 | 
	
		
			
				|  |  | +    var sign  = subC.dot( new THREE.Vector3().crossVectors( subaB, subaA ));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * three-model
 | 
	
		
			
				|  |  | - *
 | 
	
		
			
				|  |  | - * Loader for THREE.js JSON format. Somewhat confusingly, there are two
 | 
	
		
			
				|  |  | - * different THREE.js formats, both having the .json extension. This loader
 | 
	
		
			
				|  |  | - * supports both, but requires you to specify the mode as "object" or "json".
 | 
	
		
			
				|  |  | - *
 | 
	
		
			
				|  |  | - * Typically, you will use "json" for a single mesh, and "object" for a scene
 | 
	
		
			
				|  |  | - * or multiple meshes. Check the console for errors, if in doubt.
 | 
	
		
			
				|  |  | - *
 | 
	
		
			
				|  |  | - * See: https://clara.io/learn/user-guide/data_exchange/threejs_export
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -module.exports = {
 | 
	
		
			
				|  |  | -  deprecated: true,
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  schema: {
 | 
	
		
			
				|  |  | -    src:               { type: 'asset' },
 | 
	
		
			
				|  |  | -    loader:            { default: 'object', oneOf: ['object', 'json'] },
 | 
	
		
			
				|  |  | -    enableAnimation:   { default: true },
 | 
	
		
			
				|  |  | -    animation:         { default: DEFAULT_ANIMATION },
 | 
	
		
			
				|  |  | -    animationDuration: { default: 0 },
 | 
	
		
			
				|  |  | -    crossorigin:       { default: '' }
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +    // Reverse the winding if negative sign
 | 
	
		
			
				|  |  | +    if( sign < 0 ){
 | 
	
		
			
				|  |  | +      tetrahedron[0].reverse();
 | 
	
		
			
				|  |  | +      tetrahedron[1].reverse();
 | 
	
		
			
				|  |  | +      tetrahedron[2].reverse();
 | 
	
		
			
				|  |  | +      tetrahedron[3].reverse();
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  init: function () {
 | 
	
		
			
				|  |  | -    this.model = null;
 | 
	
		
			
				|  |  | -    this.mixer = null;
 | 
	
		
			
				|  |  | -    console.warn('[three-model] Component is deprecated. Use json-model or object-model instead.');
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  update: function (previousData) {
 | 
	
		
			
				|  |  | -    previousData = previousData || {};
 | 
	
		
			
				|  |  | +    //One for each face of the pyramid
 | 
	
		
			
				|  |  | +    var pointsCloned = points.slice();
 | 
	
		
			
				|  |  | +    pointsCloned.splice( pointsCloned.indexOf( v0 ), 1 );
 | 
	
		
			
				|  |  | +    pointsCloned.splice( pointsCloned.indexOf( v1 ), 1 );
 | 
	
		
			
				|  |  | +    pointsCloned.splice( pointsCloned.indexOf( v2 ), 1 );
 | 
	
		
			
				|  |  | +    pointsCloned.splice( pointsCloned.indexOf( v3 ), 1 );
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    var loader,
 | 
	
		
			
				|  |  | -        data = this.data;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (!data.src) {
 | 
	
		
			
				|  |  | -      this.remove();
 | 
	
		
			
				|  |  | -      return;
 | 
	
		
			
				|  |  | +    var i = tetrahedron.length;
 | 
	
		
			
				|  |  | +    while( i-- > 0 ){
 | 
	
		
			
				|  |  | +      assignPoints( tetrahedron[i], pointsCloned, points );
 | 
	
		
			
				|  |  | +      if( tetrahedron[i].visiblePoints !== undefined ){
 | 
	
		
			
				|  |  | +        faceStack.push( tetrahedron[i] );
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      faces.push( tetrahedron[i] );
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // First load.
 | 
	
		
			
				|  |  | -    if (!Object.keys(previousData).length) {
 | 
	
		
			
				|  |  | -      this.remove();
 | 
	
		
			
				|  |  | -      if (data.loader === 'object') {
 | 
	
		
			
				|  |  | -        loader = new THREE.ObjectLoader();
 | 
	
		
			
				|  |  | -        if (data.crossorigin) loader.setCrossOrigin(data.crossorigin);
 | 
	
		
			
				|  |  | -        loader.load(data.src, function(loaded) {
 | 
	
		
			
				|  |  | -          loaded.traverse( function(object) {
 | 
	
		
			
				|  |  | -            if (object instanceof THREE.SkinnedMesh)
 | 
	
		
			
				|  |  | -              loaded = object;
 | 
	
		
			
				|  |  | -          });
 | 
	
		
			
				|  |  | -          if(loaded.material)
 | 
	
		
			
				|  |  | -            loaded.material.skinning = !!((loaded.geometry && loaded.geometry.bones) || []).length;
 | 
	
		
			
				|  |  | -          this.load(loaded);
 | 
	
		
			
				|  |  | -        }.bind(this));
 | 
	
		
			
				|  |  | -      } else if (data.loader === 'json') {
 | 
	
		
			
				|  |  | -        loader = new THREE.JSONLoader();
 | 
	
		
			
				|  |  | -        if (data.crossorigin) loader.crossOrigin = data.crossorigin;
 | 
	
		
			
				|  |  | -        loader.load(data.src, function (geometry, materials) {
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -          // Attempt to automatically detect common material options.
 | 
	
		
			
				|  |  | -          materials.forEach(function (mat) {
 | 
	
		
			
				|  |  | -            mat.vertexColors = (geometry.faces[0] || {}).color ? THREE.FaceColors : THREE.NoColors;
 | 
	
		
			
				|  |  | -            mat.skinning = !!(geometry.bones || []).length;
 | 
	
		
			
				|  |  | -            mat.morphTargets = !!(geometry.morphTargets || []).length;
 | 
	
		
			
				|  |  | -            mat.morphNormals = !!(geometry.morphNormals || []).length;
 | 
	
		
			
				|  |  | -          });
 | 
	
		
			
				|  |  | +    process( points );
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -          var mesh = (geometry.bones || []).length
 | 
	
		
			
				|  |  | -            ? new THREE.SkinnedMesh(geometry, new THREE.MultiMaterial(materials))
 | 
	
		
			
				|  |  | -            : new THREE.Mesh(geometry, new THREE.MultiMaterial(materials));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -          this.load(mesh);
 | 
	
		
			
				|  |  | -        }.bind(this));
 | 
	
		
			
				|  |  | -      } else {
 | 
	
		
			
				|  |  | -        throw new Error('[three-model] Invalid mode "%s".', data.mode);
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -      return;
 | 
	
		
			
				|  |  | +    //  Assign to our geometry object
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    var ll = faces.length;
 | 
	
		
			
				|  |  | +    while( ll-- > 0 ){
 | 
	
		
			
				|  |  | +      geometry.faces[ll] = new THREE.Face3( faces[ll][2], faces[ll][1], faces[ll][0], faces[ll].normal )
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    var activeAction = this.model && this.model.activeAction;
 | 
	
		
			
				|  |  | +    geometry.normalsNeedUpdate = true;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (data.animation !== previousData.animation) {
 | 
	
		
			
				|  |  | -      if (activeAction) activeAction.stop();
 | 
	
		
			
				|  |  | -      this.playAnimation();
 | 
	
		
			
				|  |  | -      return;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +    return geometry;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +}())
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +},{}],86:[function(require,module,exports){
 | 
	
		
			
				|  |  | +var EPS = 0.1;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +module.exports = {
 | 
	
		
			
				|  |  | +  schema: {
 | 
	
		
			
				|  |  | +    enabled: {default: true},
 | 
	
		
			
				|  |  | +    mode: {default: 'teleport', oneOf: ['teleport', 'animate']},
 | 
	
		
			
				|  |  | +    animateSpeed: {default: 3.0}
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (activeAction && data.enableAnimation !== activeAction.isRunning()) {
 | 
	
		
			
				|  |  | -      data.enableAnimation ? this.playAnimation() : activeAction.stop();
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +  init: function () {
 | 
	
		
			
				|  |  | +    this.active = true;
 | 
	
		
			
				|  |  | +    this.checkpoint = null;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (activeAction && data.animationDuration) {
 | 
	
		
			
				|  |  | -        activeAction.setDuration(data.animationDuration);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +    this.offset = new THREE.Vector3();
 | 
	
		
			
				|  |  | +    this.position = new THREE.Vector3();
 | 
	
		
			
				|  |  | +    this.targetPosition = new THREE.Vector3();
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  load: function (model) {
 | 
	
		
			
				|  |  | -    this.model = model;
 | 
	
		
			
				|  |  | -    this.mixer = new THREE.AnimationMixer(this.model);
 | 
	
		
			
				|  |  | -    this.el.setObject3D('mesh', model);
 | 
	
		
			
				|  |  | -    this.el.emit('model-loaded', {format: 'three', model: model});
 | 
	
		
			
				|  |  | +  play: function () { this.active = true; },
 | 
	
		
			
				|  |  | +  pause: function () { this.active = false; },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (this.data.enableAnimation) this.playAnimation();
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +  setCheckpoint: function (checkpoint) {
 | 
	
		
			
				|  |  | +    var el = this.el;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  playAnimation: function () {
 | 
	
		
			
				|  |  | -    var clip,
 | 
	
		
			
				|  |  | -        data = this.data,
 | 
	
		
			
				|  |  | -        animations = this.model.animations || this.model.geometry.animations || [];
 | 
	
		
			
				|  |  | +    if (!this.active) return;
 | 
	
		
			
				|  |  | +    if (this.checkpoint === checkpoint) return;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (!data.enableAnimation || !data.animation || !animations.length) {
 | 
	
		
			
				|  |  | -      return;
 | 
	
		
			
				|  |  | +    if (this.checkpoint) {
 | 
	
		
			
				|  |  | +      el.emit('navigation-end', {checkpoint: this.checkpoint});
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    clip = data.animation === DEFAULT_ANIMATION
 | 
	
		
			
				|  |  | -      ? animations[0]
 | 
	
		
			
				|  |  | -      : THREE.AnimationClip.findByName(animations, data.animation);
 | 
	
		
			
				|  |  | +    this.checkpoint = checkpoint;
 | 
	
		
			
				|  |  | +    this.sync();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (!clip) {
 | 
	
		
			
				|  |  | -      console.error('[three-model] Animation "%s" not found.', data.animation);
 | 
	
		
			
				|  |  | +    // Ignore new checkpoint if we're already there.
 | 
	
		
			
				|  |  | +    if (this.position.distanceTo(this.targetPosition) < EPS) {
 | 
	
		
			
				|  |  | +      this.checkpoint = null;
 | 
	
		
			
				|  |  |        return;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    this.model.activeAction = this.mixer.clipAction(clip, this.model);
 | 
	
		
			
				|  |  | -    if (data.animationDuration) {
 | 
	
		
			
				|  |  | -      this.model.activeAction.setDuration(data.animationDuration);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    this.model.activeAction.play();
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  remove: function () {
 | 
	
		
			
				|  |  | -    if (this.mixer) this.mixer.stopAllAction();
 | 
	
		
			
				|  |  | -    if (this.model) this.el.removeObject3D('mesh');
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +    el.emit('navigation-start', {checkpoint: checkpoint});
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  tick: function (t, dt) {
 | 
	
		
			
				|  |  | -    if (this.mixer && !isNaN(dt)) {
 | 
	
		
			
				|  |  | -      this.mixer.update(dt / 1000);
 | 
	
		
			
				|  |  | +    if (this.data.mode === 'teleport') {
 | 
	
		
			
				|  |  | +      this.el.setAttribute('position', this.targetPosition);
 | 
	
		
			
				|  |  | +      this.checkpoint = null;
 | 
	
		
			
				|  |  | +      el.emit('navigation-end', {checkpoint: checkpoint});
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -},{}],98:[function(require,module,exports){
 | 
	
		
			
				|  |  | -module.exports = {
 | 
	
		
			
				|  |  | -  schema: {
 | 
	
		
			
				|  |  | -    offset: {default: {x: 0, y: 0, z: 0}, type: 'vec3'}
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  init: function () {
 | 
	
		
			
				|  |  | -    this.active = false;
 | 
	
		
			
				|  |  | -    this.targetEl = null;
 | 
	
		
			
				|  |  | -    this.fire = this.fire.bind(this);
 | 
	
		
			
				|  |  | -    this.offset = new THREE.Vector3();
 | 
	
		
			
				|  |  | +  isVelocityActive: function () {
 | 
	
		
			
				|  |  | +    return !!(this.active && this.checkpoint);
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  update: function () {
 | 
	
		
			
				|  |  | -    this.offset.copy(this.data.offset);
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +  getVelocity: function () {
 | 
	
		
			
				|  |  | +    if (!this.active) return;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  play: function () { this.el.addEventListener('click', this.fire); },
 | 
	
		
			
				|  |  | -  pause: function () { this.el.removeEventListener('click', this.fire); },
 | 
	
		
			
				|  |  | -  remove: function () { this.pause(); },
 | 
	
		
			
				|  |  | +    var data = this.data,
 | 
	
		
			
				|  |  | +        offset = this.offset,
 | 
	
		
			
				|  |  | +        position = this.position,
 | 
	
		
			
				|  |  | +        targetPosition = this.targetPosition,
 | 
	
		
			
				|  |  | +        checkpoint = this.checkpoint;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  fire: function () {
 | 
	
		
			
				|  |  | -    var targetEl = this.el.sceneEl.querySelector('[checkpoint-controls]');
 | 
	
		
			
				|  |  | -    if (!targetEl) {
 | 
	
		
			
				|  |  | -      throw new Error('No `checkpoint-controls` component found.');
 | 
	
		
			
				|  |  | +    this.sync();
 | 
	
		
			
				|  |  | +    if (position.distanceTo(targetPosition) < EPS) {
 | 
	
		
			
				|  |  | +      this.checkpoint = null;
 | 
	
		
			
				|  |  | +      this.el.emit('navigation-end', {checkpoint: checkpoint});
 | 
	
		
			
				|  |  | +      return offset.set(0, 0, 0);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -    targetEl.components['checkpoint-controls'].setCheckpoint(this.el);
 | 
	
		
			
				|  |  | +    offset.setLength(data.animateSpeed);
 | 
	
		
			
				|  |  | +    return offset;
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  getOffset: function () {
 | 
	
		
			
				|  |  | -    return this.offset.copy(this.data.offset);
 | 
	
		
			
				|  |  | +  sync: function () {
 | 
	
		
			
				|  |  | +    var offset = this.offset,
 | 
	
		
			
				|  |  | +        position = this.position,
 | 
	
		
			
				|  |  | +        targetPosition = this.targetPosition;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    position.copy(this.el.getAttribute('position'));
 | 
	
		
			
				|  |  | +    targetPosition.copy(this.checkpoint.object3D.getWorldPosition());
 | 
	
		
			
				|  |  | +    targetPosition.add(this.checkpoint.components.checkpoint.getOffset());
 | 
	
		
			
				|  |  | +    offset.copy(targetPosition).sub(position);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{}],99:[function(require,module,exports){
 | 
	
		
			
				|  |  | +},{}],87:[function(require,module,exports){
 | 
	
		
			
				|  |  |  /**
 | 
	
		
			
				|  |  | - * Specifies an envMap on an entity, without replacing any existing material
 | 
	
		
			
				|  |  | - * properties.
 | 
	
		
			
				|  |  | + * Gamepad controls for A-Frame.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Stripped-down version of: https://github.com/donmccurdy/aframe-gamepad-controls
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * For more information about the Gamepad API, see:
 | 
	
		
			
				|  |  | + * https://developer.mozilla.org/en-US/docs/Web/API/Gamepad_API/Using_the_Gamepad_API
 | 
	
		
			
				|  |  |   */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +var GamepadButton = require('../../lib/GamepadButton'),
 | 
	
		
			
				|  |  | +    GamepadButtonEvent = require('../../lib/GamepadButtonEvent');
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +var JOYSTICK_EPS = 0.2;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  module.exports = {
 | 
	
		
			
				|  |  | -  schema: {
 | 
	
		
			
				|  |  | -    path: {default: ''},
 | 
	
		
			
				|  |  | -    extension: {default: 'jpg'},
 | 
	
		
			
				|  |  | -    format: {default: 'RGBFormat'},
 | 
	
		
			
				|  |  | -    enableBackground: {default: false}
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  init: function () {
 | 
	
		
			
				|  |  | -    var data = this.data;
 | 
	
		
			
				|  |  | +  /*******************************************************************
 | 
	
		
			
				|  |  | +   * Statics
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    this.texture = new THREE.CubeTextureLoader().load([
 | 
	
		
			
				|  |  | -      data.path + 'posx.' + data.extension, data.path + 'negx.' + data.extension,
 | 
	
		
			
				|  |  | -      data.path + 'posy.' + data.extension, data.path + 'negy.' + data.extension,
 | 
	
		
			
				|  |  | -      data.path + 'posz.' + data.extension, data.path + 'negz.' + data.extension
 | 
	
		
			
				|  |  | -    ]);
 | 
	
		
			
				|  |  | -    this.texture.format = THREE[data.format];
 | 
	
		
			
				|  |  | +  GamepadButton: GamepadButton,
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (data.enableBackground) {
 | 
	
		
			
				|  |  | -      this.el.sceneEl.object3D.background = this.texture;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +  /*******************************************************************
 | 
	
		
			
				|  |  | +   * Schema
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    this.applyEnvMap();
 | 
	
		
			
				|  |  | -    this.el.addEventListener('object3dset', this.applyEnvMap.bind(this));
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +  schema: {
 | 
	
		
			
				|  |  | +    // Controller 0-3
 | 
	
		
			
				|  |  | +    controller:        { default: 0, oneOf: [0, 1, 2, 3] },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  applyEnvMap: function () {
 | 
	
		
			
				|  |  | -    var mesh = this.el.getObject3D('mesh');
 | 
	
		
			
				|  |  | -    var envMap = this.texture;
 | 
	
		
			
				|  |  | +    // Enable/disable features
 | 
	
		
			
				|  |  | +    enabled:           { default: true },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (!mesh) return;
 | 
	
		
			
				|  |  | +    // Debugging
 | 
	
		
			
				|  |  | +    debug:             { default: false }
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    mesh.traverse(function (node) {
 | 
	
		
			
				|  |  | -      if (node.material && 'envMap' in node.material) {
 | 
	
		
			
				|  |  | -        node.material.envMap = envMap;
 | 
	
		
			
				|  |  | -        node.material.needsUpdate = true;
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -    });
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | +  /*******************************************************************
 | 
	
		
			
				|  |  | +   * Core
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{}],100:[function(require,module,exports){
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * Based on aframe/examples/showcase/tracked-controls.
 | 
	
		
			
				|  |  | - *
 | 
	
		
			
				|  |  | - * Handles events coming from the hand-controls.
 | 
	
		
			
				|  |  | - * Determines if the entity is grabbed or released.
 | 
	
		
			
				|  |  | - * Updates its position to move along the controller.
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -module.exports = {
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * Called once when component is attached. Generally for initial setup.
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  |    init: function () {
 | 
	
		
			
				|  |  | -    this.GRABBED_STATE = 'grabbed';
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    this.grabbing = false;
 | 
	
		
			
				|  |  | -    this.hitEl =      /** @type {AFRAME.Element}    */ null;
 | 
	
		
			
				|  |  | -    this.physics =    /** @type {AFRAME.System}     */ this.el.sceneEl.systems.physics;
 | 
	
		
			
				|  |  | -    this.constraint = /** @type {CANNON.Constraint} */ null;
 | 
	
		
			
				|  |  | +    var scene = this.el.sceneEl;
 | 
	
		
			
				|  |  | +    this.prevTime = window.performance.now();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // Bind event handlers
 | 
	
		
			
				|  |  | -    this.onHit = this.onHit.bind(this);
 | 
	
		
			
				|  |  | -    this.onGripOpen = this.onGripOpen.bind(this);
 | 
	
		
			
				|  |  | -    this.onGripClose = this.onGripClose.bind(this);
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +    // Button state
 | 
	
		
			
				|  |  | +    this.buttons = {};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  play: function () {
 | 
	
		
			
				|  |  | -    var el = this.el;
 | 
	
		
			
				|  |  | -    el.addEventListener('hit', this.onHit);
 | 
	
		
			
				|  |  | -    el.addEventListener('gripdown', this.onGripClose);
 | 
	
		
			
				|  |  | -    el.addEventListener('gripup', this.onGripOpen);
 | 
	
		
			
				|  |  | -    el.addEventListener('trackpaddown', this.onGripClose);
 | 
	
		
			
				|  |  | -    el.addEventListener('trackpadup', this.onGripOpen);
 | 
	
		
			
				|  |  | -    el.addEventListener('triggerdown', this.onGripClose);
 | 
	
		
			
				|  |  | -    el.addEventListener('triggerup', this.onGripOpen);
 | 
	
		
			
				|  |  | +    scene.addBehavior(this);
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  pause: function () {
 | 
	
		
			
				|  |  | -    var el = this.el;
 | 
	
		
			
				|  |  | -    el.removeEventListener('hit', this.onHit);
 | 
	
		
			
				|  |  | -    el.removeEventListener('gripdown', this.onGripClose);
 | 
	
		
			
				|  |  | -    el.removeEventListener('gripup', this.onGripOpen);
 | 
	
		
			
				|  |  | -    el.removeEventListener('trackpaddown', this.onGripClose);
 | 
	
		
			
				|  |  | -    el.removeEventListener('trackpadup', this.onGripOpen);
 | 
	
		
			
				|  |  | -    el.removeEventListener('triggerdown', this.onGripClose);
 | 
	
		
			
				|  |  | -    el.removeEventListener('triggerup', this.onGripOpen);
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * Called when component is attached and when component data changes.
 | 
	
		
			
				|  |  | +   * Generally modifies the entity based on the data.
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  update: function () { this.tick(); },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  onGripClose: function (evt) {
 | 
	
		
			
				|  |  | -    this.grabbing = true;
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * Called on each iteration of main render loop.
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  tick: function () {
 | 
	
		
			
				|  |  | +    this.updateButtonState();
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  onGripOpen: function (evt) {
 | 
	
		
			
				|  |  | -    var hitEl = this.hitEl;
 | 
	
		
			
				|  |  | -    this.grabbing = false;
 | 
	
		
			
				|  |  | -    if (!hitEl) { return; }
 | 
	
		
			
				|  |  | -    hitEl.removeState(this.GRABBED_STATE);
 | 
	
		
			
				|  |  | -    this.hitEl = undefined;
 | 
	
		
			
				|  |  | -    this.physics.world.removeConstraint(this.constraint);
 | 
	
		
			
				|  |  | -    this.constraint = null;
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * Called when a component is removed (e.g., via removeAttribute).
 | 
	
		
			
				|  |  | +   * Generally undoes all modifications to the entity.
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  remove: function () { },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  onHit: function (evt) {
 | 
	
		
			
				|  |  | -    var hitEl = evt.detail.el;
 | 
	
		
			
				|  |  | -    // If the element is already grabbed (it could be grabbed by another controller).
 | 
	
		
			
				|  |  | -    // If the hand is not grabbing the element does not stick.
 | 
	
		
			
				|  |  | -    // If we're already grabbing something you can't grab again.
 | 
	
		
			
				|  |  | -    if (!hitEl || hitEl.is(this.GRABBED_STATE) || !this.grabbing || this.hitEl) { return; }
 | 
	
		
			
				|  |  | -    hitEl.addState(this.GRABBED_STATE);
 | 
	
		
			
				|  |  | -    this.hitEl = hitEl;
 | 
	
		
			
				|  |  | -    this.constraint = new CANNON.LockConstraint(this.el.body, hitEl.body);
 | 
	
		
			
				|  |  | -    this.physics.world.addConstraint(this.constraint);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | +  /*******************************************************************
 | 
	
		
			
				|  |  | +   * Universal controls - movement
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{}],101:[function(require,module,exports){
 | 
	
		
			
				|  |  | -var physics = require('aframe-physics-system');
 | 
	
		
			
				|  |  | +  isVelocityActive: function () {
 | 
	
		
			
				|  |  | +    if (!this.data.enabled || !this.isConnected()) return false;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -module.exports = {
 | 
	
		
			
				|  |  | -  'checkpoint':      require('./checkpoint'),
 | 
	
		
			
				|  |  | -  'cube-env-map':    require('./cube-env-map'),
 | 
	
		
			
				|  |  | -  'grab':            require('./grab'),
 | 
	
		
			
				|  |  | -  'jump-ability':    require('./jump-ability'),
 | 
	
		
			
				|  |  | -  'kinematic-body':  require('./kinematic-body'),
 | 
	
		
			
				|  |  | -  'mesh-smooth':     require('./mesh-smooth'),
 | 
	
		
			
				|  |  | -  'sphere-collider': require('./sphere-collider'),
 | 
	
		
			
				|  |  | -  'toggle-velocity': require('./toggle-velocity'),
 | 
	
		
			
				|  |  | +    var dpad = this.getDpad(),
 | 
	
		
			
				|  |  | +        joystick0 = this.getJoystick(0),
 | 
	
		
			
				|  |  | +        inputX = dpad.x || joystick0.x,
 | 
	
		
			
				|  |  | +        inputY = dpad.y || joystick0.y;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  registerAll: function (AFRAME) {
 | 
	
		
			
				|  |  | -    if (this._registered) return;
 | 
	
		
			
				|  |  | +    return Math.abs(inputX) > JOYSTICK_EPS || Math.abs(inputY) > JOYSTICK_EPS;
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    AFRAME = AFRAME || window.AFRAME;
 | 
	
		
			
				|  |  | +  getVelocityDelta: function () {
 | 
	
		
			
				|  |  | +    var dpad = this.getDpad(),
 | 
	
		
			
				|  |  | +        joystick0 = this.getJoystick(0),
 | 
	
		
			
				|  |  | +        inputX = dpad.x || joystick0.x,
 | 
	
		
			
				|  |  | +        inputY = dpad.y || joystick0.y,
 | 
	
		
			
				|  |  | +        dVelocity = new THREE.Vector3();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    physics.registerAll();
 | 
	
		
			
				|  |  | -    if (!AFRAME.components['checkpoint'])      AFRAME.registerComponent('checkpoint',      this['checkpoint']);
 | 
	
		
			
				|  |  | -    if (!AFRAME.components['cube-env-map'])    AFRAME.registerComponent('cube-env-map',    this['cube-env-map']);
 | 
	
		
			
				|  |  | -    if (!AFRAME.components['grab'])            AFRAME.registerComponent('grab',            this['grab']);
 | 
	
		
			
				|  |  | -    if (!AFRAME.components['jump-ability'])    AFRAME.registerComponent('jump-ability',    this['jump-ability']);
 | 
	
		
			
				|  |  | -    if (!AFRAME.components['kinematic-body'])  AFRAME.registerComponent('kinematic-body',  this['kinematic-body']);
 | 
	
		
			
				|  |  | -    if (!AFRAME.components['mesh-smooth'])     AFRAME.registerComponent('mesh-smooth',     this['mesh-smooth']);
 | 
	
		
			
				|  |  | -    if (!AFRAME.components['sphere-collider']) AFRAME.registerComponent('sphere-collider', this['sphere-collider']);
 | 
	
		
			
				|  |  | -    if (!AFRAME.components['toggle-velocity']) AFRAME.registerComponent('toggle-velocity', this['toggle-velocity']);
 | 
	
		
			
				|  |  | +    if (Math.abs(inputX) > JOYSTICK_EPS) {
 | 
	
		
			
				|  |  | +      dVelocity.x += inputX;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    if (Math.abs(inputY) > JOYSTICK_EPS) {
 | 
	
		
			
				|  |  | +      dVelocity.z += inputY;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    this._registered = true;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | +    return dVelocity;
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{"./checkpoint":98,"./cube-env-map":99,"./grab":100,"./jump-ability":102,"./kinematic-body":103,"./mesh-smooth":104,"./sphere-collider":105,"./toggle-velocity":106,"aframe-physics-system":11}],102:[function(require,module,exports){
 | 
	
		
			
				|  |  | -var ACCEL_G = -9.8, // m/s^2
 | 
	
		
			
				|  |  | -    EASING = -15; // m/s^2
 | 
	
		
			
				|  |  | +  /*******************************************************************
 | 
	
		
			
				|  |  | +   * Universal controls - rotation
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * Jump ability.
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -module.exports = {
 | 
	
		
			
				|  |  | -  dependencies: ['velocity'],
 | 
	
		
			
				|  |  | +  isRotationActive: function () {
 | 
	
		
			
				|  |  | +    if (!this.data.enabled || !this.isConnected()) return false;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /* Schema
 | 
	
		
			
				|  |  | -  ——————————————————————————————————————————————*/
 | 
	
		
			
				|  |  | +    var joystick1 = this.getJoystick(1);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  schema: {
 | 
	
		
			
				|  |  | -    on: { default: 'keydown:Space gamepadbuttondown:0' },
 | 
	
		
			
				|  |  | -    playerHeight: { default: 1.764 },
 | 
	
		
			
				|  |  | -    maxJumps: { default: 1 },
 | 
	
		
			
				|  |  | -    distance: { default: 5 },
 | 
	
		
			
				|  |  | -    soundJump: { default: '' },
 | 
	
		
			
				|  |  | -    soundLand: { default: '' },
 | 
	
		
			
				|  |  | -    debug: { default: false }
 | 
	
		
			
				|  |  | +    return Math.abs(joystick1.x) > JOYSTICK_EPS || Math.abs(joystick1.y) > JOYSTICK_EPS;
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  init: function () {
 | 
	
		
			
				|  |  | -    this.velocity = 0;
 | 
	
		
			
				|  |  | -    this.numJumps = 0;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    var beginJump = this.beginJump.bind(this),
 | 
	
		
			
				|  |  | -        events = this.data.on.split(' ');
 | 
	
		
			
				|  |  | -    this.bindings = {};
 | 
	
		
			
				|  |  | -    for (var i = 0; i <  events.length; i++) {
 | 
	
		
			
				|  |  | -      this.bindings[events[i]] = beginJump;
 | 
	
		
			
				|  |  | -      this.el.addEventListener(events[i], beginJump);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    this.bindings.collide = this.onCollide.bind(this);
 | 
	
		
			
				|  |  | -    this.el.addEventListener('collide', this.bindings.collide);
 | 
	
		
			
				|  |  | +  getRotationDelta: function () {
 | 
	
		
			
				|  |  | +    var lookVector = this.getJoystick(1);
 | 
	
		
			
				|  |  | +    if (Math.abs(lookVector.x) <= JOYSTICK_EPS) lookVector.x = 0;
 | 
	
		
			
				|  |  | +    if (Math.abs(lookVector.y) <= JOYSTICK_EPS) lookVector.y = 0;
 | 
	
		
			
				|  |  | +    return lookVector;
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  remove: function () {
 | 
	
		
			
				|  |  | -    for (var event in this.bindings) {
 | 
	
		
			
				|  |  | -      if (this.bindings.hasOwnProperty(event)) {
 | 
	
		
			
				|  |  | -        this.el.removeEventListener(event, this.bindings[event]);
 | 
	
		
			
				|  |  | -        delete this.bindings[event];
 | 
	
		
			
				|  |  | +  /*******************************************************************
 | 
	
		
			
				|  |  | +   * Button events
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  updateButtonState: function () {
 | 
	
		
			
				|  |  | +    var gamepad = this.getGamepad();
 | 
	
		
			
				|  |  | +    if (this.data.enabled && gamepad) {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      // Fire DOM events for button state changes.
 | 
	
		
			
				|  |  | +      for (var i = 0; i < gamepad.buttons.length; i++) {
 | 
	
		
			
				|  |  | +        if (gamepad.buttons[i].pressed && !this.buttons[i]) {
 | 
	
		
			
				|  |  | +          this.emit(new GamepadButtonEvent('gamepadbuttondown', i, gamepad.buttons[i]));
 | 
	
		
			
				|  |  | +        } else if (!gamepad.buttons[i].pressed && this.buttons[i]) {
 | 
	
		
			
				|  |  | +          this.emit(new GamepadButtonEvent('gamepadbuttonup', i, gamepad.buttons[i]));
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        this.buttons[i] = gamepad.buttons[i].pressed;
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    this.el.removeEventListener('collide', this.bindings.collide);
 | 
	
		
			
				|  |  | -    delete this.bindings.collide;
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  beginJump: function () {
 | 
	
		
			
				|  |  | -    if (this.numJumps < this.data.maxJumps) {
 | 
	
		
			
				|  |  | -      var data = this.data,
 | 
	
		
			
				|  |  | -          initialVelocity = Math.sqrt(-2 * data.distance * (ACCEL_G + EASING)),
 | 
	
		
			
				|  |  | -          v = this.el.getAttribute('velocity');
 | 
	
		
			
				|  |  | -      this.el.setAttribute('velocity', {x: v.x, y: initialVelocity, z: v.z});
 | 
	
		
			
				|  |  | -      this.numJumps++;
 | 
	
		
			
				|  |  | +    } else if (Object.keys(this.buttons)) {
 | 
	
		
			
				|  |  | +      // Reset state if controls are disabled or controller is lost.
 | 
	
		
			
				|  |  | +      this.buttons = {};
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  onCollide: function () {
 | 
	
		
			
				|  |  | -    this.numJumps = 0;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -},{}],103:[function(require,module,exports){
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * Kinematic body.
 | 
	
		
			
				|  |  | - *
 | 
	
		
			
				|  |  | - * Managed dynamic body, which moves but is not affected (directly) by the
 | 
	
		
			
				|  |  | - * physics engine. This is not a true kinematic body, in the sense that we are
 | 
	
		
			
				|  |  | - * letting the physics engine _compute_ collisions against it and selectively
 | 
	
		
			
				|  |  | - * applying those collisions to the object. The physics engine does not decide
 | 
	
		
			
				|  |  | - * the position/velocity/rotation of the element.
 | 
	
		
			
				|  |  | - *
 | 
	
		
			
				|  |  | - * Used for the camera object, because full physics simulation would create
 | 
	
		
			
				|  |  | - * movement that feels unnatural to the player. Bipedal movement does not
 | 
	
		
			
				|  |  | - * translate nicely to rigid body physics.
 | 
	
		
			
				|  |  | - *
 | 
	
		
			
				|  |  | - * See: http://www.learn-cocos2d.com/2013/08/physics-engine-platformer-terrible-idea/
 | 
	
		
			
				|  |  | - * And: http://oxleygamedev.blogspot.com/2011/04/player-physics-part-2.html
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -var CANNON = window.CANNON;
 | 
	
		
			
				|  |  | -var EPS = 0.000001;
 | 
	
		
			
				|  |  | +  emit: function (event) {
 | 
	
		
			
				|  |  | +    // Emit original event.
 | 
	
		
			
				|  |  | +    this.el.emit(event.type, event);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -module.exports = {
 | 
	
		
			
				|  |  | -  dependencies: ['velocity'],
 | 
	
		
			
				|  |  | +    // Emit convenience event, identifying button index.
 | 
	
		
			
				|  |  | +    this.el.emit(
 | 
	
		
			
				|  |  | +      event.type + ':' + event.index,
 | 
	
		
			
				|  |  | +      new GamepadButtonEvent(event.type, event.index, event)
 | 
	
		
			
				|  |  | +    );
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    /*******************************************************************
 | 
	
		
			
				|  |  | -   * Schema
 | 
	
		
			
				|  |  | +   * Gamepad state
 | 
	
		
			
				|  |  |     */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  schema: {
 | 
	
		
			
				|  |  | -    mass:           { default: 5 },
 | 
	
		
			
				|  |  | -    radius:         { default: 1.3 },
 | 
	
		
			
				|  |  | -    height:         { default: 1.764 },
 | 
	
		
			
				|  |  | -    linearDamping:  { default: 0.05 },
 | 
	
		
			
				|  |  | -    enableSlopes:   { default: true }
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * Returns the Gamepad instance attached to the component. If connected,
 | 
	
		
			
				|  |  | +   * a proxy-controls component may provide access to Gamepad input from a
 | 
	
		
			
				|  |  | +   * remote device.
 | 
	
		
			
				|  |  | +   *
 | 
	
		
			
				|  |  | +   * @return {Gamepad}
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  getGamepad: function () {
 | 
	
		
			
				|  |  | +    var localGamepad = navigator.getGamepads
 | 
	
		
			
				|  |  | +          && navigator.getGamepads()[this.data.controller],
 | 
	
		
			
				|  |  | +        proxyControls = this.el.sceneEl.components['proxy-controls'],
 | 
	
		
			
				|  |  | +        proxyGamepad = proxyControls && proxyControls.isConnected()
 | 
	
		
			
				|  |  | +          && proxyControls.getGamepad(this.data.controller);
 | 
	
		
			
				|  |  | +    return proxyGamepad || localGamepad;
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /*******************************************************************
 | 
	
		
			
				|  |  | -   * Lifecycle
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * Returns the state of the given button.
 | 
	
		
			
				|  |  | +   * @param  {number} index The button (0-N) for which to find state.
 | 
	
		
			
				|  |  | +   * @return {GamepadButton}
 | 
	
		
			
				|  |  |     */
 | 
	
		
			
				|  |  | +  getButton: function (index) {
 | 
	
		
			
				|  |  | +    return this.getGamepad().buttons[index];
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  init: function () {
 | 
	
		
			
				|  |  | -    this.system = this.el.sceneEl.systems.physics;
 | 
	
		
			
				|  |  | -    this.system.addBehavior(this, this.system.Phase.SIMULATE);
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * Returns state of the given axis. Axes are labelled 0-N, where 0-1 will
 | 
	
		
			
				|  |  | +   * represent X/Y on the first joystick, and 2-3 X/Y on the second.
 | 
	
		
			
				|  |  | +   * @param  {number} index The axis (0-N) for which to find state.
 | 
	
		
			
				|  |  | +   * @return {number} On the interval [-1,1].
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  getAxis: function (index) {
 | 
	
		
			
				|  |  | +    return this.getGamepad().axes[index];
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    var el = this.el,
 | 
	
		
			
				|  |  | -        data = this.data,
 | 
	
		
			
				|  |  | -        position = (new CANNON.Vec3()).copy(el.getAttribute('position'));
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * Returns the state of the given joystick (0 or 1) as a THREE.Vector2.
 | 
	
		
			
				|  |  | +   * @param  {number} id The joystick (0, 1) for which to find state.
 | 
	
		
			
				|  |  | +   * @return {THREE.Vector2}
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  getJoystick: function (index) {
 | 
	
		
			
				|  |  | +    var gamepad = this.getGamepad();
 | 
	
		
			
				|  |  | +    switch (index) {
 | 
	
		
			
				|  |  | +      case 0: return new THREE.Vector2(gamepad.axes[0], gamepad.axes[1]);
 | 
	
		
			
				|  |  | +      case 1: return new THREE.Vector2(gamepad.axes[2], gamepad.axes[3]);
 | 
	
		
			
				|  |  | +      default: throw new Error('Unexpected joystick index "%d".', index);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    this.body = new CANNON.Body({
 | 
	
		
			
				|  |  | -      material: this.system.material,
 | 
	
		
			
				|  |  | -      position: position,
 | 
	
		
			
				|  |  | -      mass: data.mass,
 | 
	
		
			
				|  |  | -      linearDamping: data.linearDamping,
 | 
	
		
			
				|  |  | -      fixedRotation: true
 | 
	
		
			
				|  |  | -    });
 | 
	
		
			
				|  |  | -    this.body.addShape(
 | 
	
		
			
				|  |  | -      new CANNON.Sphere(data.radius),
 | 
	
		
			
				|  |  | -      new CANNON.Vec3(0, data.radius - data.height, 0)
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * Returns the state of the dpad as a THREE.Vector2.
 | 
	
		
			
				|  |  | +   * @return {THREE.Vector2}
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  getDpad: function () {
 | 
	
		
			
				|  |  | +    var gamepad = this.getGamepad();
 | 
	
		
			
				|  |  | +    if (!gamepad.buttons[GamepadButton.DPAD_RIGHT]) {
 | 
	
		
			
				|  |  | +      return new THREE.Vector2();
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    return new THREE.Vector2(
 | 
	
		
			
				|  |  | +      (gamepad.buttons[GamepadButton.DPAD_RIGHT].pressed ? 1 : 0)
 | 
	
		
			
				|  |  | +      + (gamepad.buttons[GamepadButton.DPAD_LEFT].pressed ? -1 : 0),
 | 
	
		
			
				|  |  | +      (gamepad.buttons[GamepadButton.DPAD_UP].pressed ? -1 : 0)
 | 
	
		
			
				|  |  | +      + (gamepad.buttons[GamepadButton.DPAD_DOWN].pressed ? 1 : 0)
 | 
	
		
			
				|  |  |      );
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    this.body.el = this.el;
 | 
	
		
			
				|  |  | -    this.el.body = this.body;
 | 
	
		
			
				|  |  | -    this.system.addBody(this.body);
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * Returns true if the gamepad is currently connected to the system.
 | 
	
		
			
				|  |  | +   * @return {boolean}
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  isConnected: function () {
 | 
	
		
			
				|  |  | +    var gamepad = this.getGamepad();
 | 
	
		
			
				|  |  | +    return !!(gamepad && gamepad.connected);
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  remove: function () {
 | 
	
		
			
				|  |  | -    this.system.removeBody(this.body);
 | 
	
		
			
				|  |  | -    this.system.removeBehavior(this, this.system.Phase.SIMULATE);
 | 
	
		
			
				|  |  | -    delete this.el.body;
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * Returns a string containing some information about the controller. Result
 | 
	
		
			
				|  |  | +   * may vary across browsers, for a given controller.
 | 
	
		
			
				|  |  | +   * @return {string}
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  getID: function () {
 | 
	
		
			
				|  |  | +    return this.getGamepad().id;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +},{"../../lib/GamepadButton":4,"../../lib/GamepadButtonEvent":5}],88:[function(require,module,exports){
 | 
	
		
			
				|  |  | +var radToDeg = THREE.Math.radToDeg,
 | 
	
		
			
				|  |  | +    isMobile = AFRAME.utils.device.isMobile();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +module.exports = {
 | 
	
		
			
				|  |  | +  schema: {
 | 
	
		
			
				|  |  | +    enabled: {default: true},
 | 
	
		
			
				|  |  | +    standing: {default: true}
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /*******************************************************************
 | 
	
		
			
				|  |  | -   * Tick
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | +  init: function () {
 | 
	
		
			
				|  |  | +    this.isPositionCalibrated = false;
 | 
	
		
			
				|  |  | +    this.dolly = new THREE.Object3D();
 | 
	
		
			
				|  |  | +    this.hmdEuler = new THREE.Euler();
 | 
	
		
			
				|  |  | +    this.previousHMDPosition = new THREE.Vector3();
 | 
	
		
			
				|  |  | +    this.deltaHMDPosition = new THREE.Vector3();
 | 
	
		
			
				|  |  | +    this.vrControls = new THREE.VRControls(this.dolly);
 | 
	
		
			
				|  |  | +    this.rotation = new THREE.Vector3();
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /**
 | 
	
		
			
				|  |  | -   * Checks CANNON.World for collisions and attempts to apply them to the
 | 
	
		
			
				|  |  | -   * element automatically, in a player-friendly way.
 | 
	
		
			
				|  |  | -   *
 | 
	
		
			
				|  |  | -   * There's extra logic for horizontal surfaces here. The basic requirements:
 | 
	
		
			
				|  |  | -   * (1) Only apply gravity when not in contact with _any_ horizontal surface.
 | 
	
		
			
				|  |  | -   * (2) When moving, project the velocity against exactly one ground surface.
 | 
	
		
			
				|  |  | -   *     If in contact with two ground surfaces (e.g. ground + ramp), choose
 | 
	
		
			
				|  |  | -   *     the one that collides with current velocity, if any.
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | -  step: (function () {
 | 
	
		
			
				|  |  | -    var velocity = new THREE.Vector3(),
 | 
	
		
			
				|  |  | -        normalizedVelocity = new THREE.Vector3(),
 | 
	
		
			
				|  |  | -        currentSurfaceNormal = new THREE.Vector3(),
 | 
	
		
			
				|  |  | -        groundNormal = new THREE.Vector3();
 | 
	
		
			
				|  |  | +  update: function () {
 | 
	
		
			
				|  |  | +    var data = this.data;
 | 
	
		
			
				|  |  | +    var vrControls = this.vrControls;
 | 
	
		
			
				|  |  | +    vrControls.standing = data.standing;
 | 
	
		
			
				|  |  | +    vrControls.update();
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    return function (t, dt) {
 | 
	
		
			
				|  |  | -      if (!dt) return;
 | 
	
		
			
				|  |  | +  tick: function () {
 | 
	
		
			
				|  |  | +    this.vrControls.update();
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      var body = this.body,
 | 
	
		
			
				|  |  | -          data = this.data,
 | 
	
		
			
				|  |  | -          didCollide = false,
 | 
	
		
			
				|  |  | -          height, groundHeight = -Infinity,
 | 
	
		
			
				|  |  | -          groundBody;
 | 
	
		
			
				|  |  | +  remove: function () {
 | 
	
		
			
				|  |  | +    this.vrControls.dispose();
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      dt = Math.min(dt, this.system.data.maxInterval * 1000);
 | 
	
		
			
				|  |  | +  isRotationActive: function () {
 | 
	
		
			
				|  |  | +    var hmdEuler = this.hmdEuler;
 | 
	
		
			
				|  |  | +    if (!this.data.enabled || !(this.el.sceneEl.is('vr-mode') || isMobile)) {
 | 
	
		
			
				|  |  | +      return false;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    hmdEuler.setFromQuaternion(this.dolly.quaternion, 'YXZ');
 | 
	
		
			
				|  |  | +    return !isNullVector(hmdEuler);
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      groundNormal.set(0, 0, 0);
 | 
	
		
			
				|  |  | -      velocity.copy(this.el.getAttribute('velocity'));
 | 
	
		
			
				|  |  | -      body.velocity.copy(velocity);
 | 
	
		
			
				|  |  | -      body.position.copy(this.el.getAttribute('position'));
 | 
	
		
			
				|  |  | +  getRotation: function () {
 | 
	
		
			
				|  |  | +    var hmdEuler = this.hmdEuler;
 | 
	
		
			
				|  |  | +    return this.rotation.set(
 | 
	
		
			
				|  |  | +      radToDeg(hmdEuler.x),
 | 
	
		
			
				|  |  | +      radToDeg(hmdEuler.y),
 | 
	
		
			
				|  |  | +      radToDeg(hmdEuler.z)
 | 
	
		
			
				|  |  | +    );
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      for (var i = 0, contact; (contact = this.system.world.contacts[i]); i++) {
 | 
	
		
			
				|  |  | -        // 1. Find any collisions involving this element. Get the contact
 | 
	
		
			
				|  |  | -        // normal, and make sure it's oriented _out_ of the other object.
 | 
	
		
			
				|  |  | -        if (body.id === contact.bi.id) {
 | 
	
		
			
				|  |  | -          contact.ni.negate(currentSurfaceNormal);
 | 
	
		
			
				|  |  | -        } else if (body.id === contact.bj.id) {
 | 
	
		
			
				|  |  | -          currentSurfaceNormal.copy(contact.ni);
 | 
	
		
			
				|  |  | -        } else {
 | 
	
		
			
				|  |  | -          continue;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | +  isVelocityActive: function () {
 | 
	
		
			
				|  |  | +    var deltaHMDPosition = this.deltaHMDPosition;
 | 
	
		
			
				|  |  | +    var previousHMDPosition = this.previousHMDPosition;
 | 
	
		
			
				|  |  | +    var currentHMDPosition = this.calculateHMDPosition();
 | 
	
		
			
				|  |  | +    this.isPositionCalibrated = this.isPositionCalibrated || !isNullVector(previousHMDPosition);
 | 
	
		
			
				|  |  | +    if (!this.data.enabled || !this.el.sceneEl.is('vr-mode') || isMobile) {
 | 
	
		
			
				|  |  | +      return false;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    deltaHMDPosition.copy(currentHMDPosition).sub(previousHMDPosition);
 | 
	
		
			
				|  |  | +    previousHMDPosition.copy(currentHMDPosition);
 | 
	
		
			
				|  |  | +    return this.isPositionCalibrated && !isNullVector(deltaHMDPosition);
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        didCollide = body.velocity.dot(currentSurfaceNormal) < -EPS;
 | 
	
		
			
				|  |  | -        if (didCollide && currentSurfaceNormal.y <= 0.5) {
 | 
	
		
			
				|  |  | -          // 2. If current trajectory attempts to move _through_ another
 | 
	
		
			
				|  |  | -          // object, project the velocity against the collision plane to
 | 
	
		
			
				|  |  | -          // prevent passing through.
 | 
	
		
			
				|  |  | -          velocity = velocity.projectOnPlane(currentSurfaceNormal);
 | 
	
		
			
				|  |  | -        } else if (currentSurfaceNormal.y > 0.5) {
 | 
	
		
			
				|  |  | -          // 3. If in contact with something roughly horizontal (+/- 45º) then
 | 
	
		
			
				|  |  | -          // consider that the current ground. Only the highest qualifying
 | 
	
		
			
				|  |  | -          // ground is retained.
 | 
	
		
			
				|  |  | -          height = body.id === contact.bi.id
 | 
	
		
			
				|  |  | -            ? Math.abs(contact.rj.y + contact.bj.position.y)
 | 
	
		
			
				|  |  | -            : Math.abs(contact.ri.y + contact.bi.position.y);
 | 
	
		
			
				|  |  | -          if (height > groundHeight) {
 | 
	
		
			
				|  |  | -            groundHeight = height;
 | 
	
		
			
				|  |  | -            groundNormal.copy(currentSurfaceNormal);
 | 
	
		
			
				|  |  | -            groundBody = body.id === contact.bi.id ? contact.bj : contact.bi;
 | 
	
		
			
				|  |  | -          }
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | +  getPositionDelta: function () {
 | 
	
		
			
				|  |  | +    return this.deltaHMDPosition;
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      normalizedVelocity.copy(velocity).normalize();
 | 
	
		
			
				|  |  | -      if (groundBody && normalizedVelocity.y < 0.5) {
 | 
	
		
			
				|  |  | -        if (!data.enableSlopes) {
 | 
	
		
			
				|  |  | -          groundNormal.set(0, 1, 0);
 | 
	
		
			
				|  |  | -        } else if (groundNormal.y < 1 - EPS) {
 | 
	
		
			
				|  |  | -          groundNormal.copy(this.raycastToGround(groundBody, groundNormal));
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | +  calculateHMDPosition: function () {
 | 
	
		
			
				|  |  | +    var dolly = this.dolly;
 | 
	
		
			
				|  |  | +    var position = new THREE.Vector3();
 | 
	
		
			
				|  |  | +    dolly.updateMatrix();
 | 
	
		
			
				|  |  | +    position.setFromMatrixPosition(dolly.matrix);
 | 
	
		
			
				|  |  | +    return position;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        // 4. Project trajectory onto the top-most ground object, unless
 | 
	
		
			
				|  |  | -        // trajectory is > 45º.
 | 
	
		
			
				|  |  | -        velocity = velocity.projectOnPlane(groundNormal);
 | 
	
		
			
				|  |  | -      } else {
 | 
	
		
			
				|  |  | -        // 5. If not in contact with anything horizontal, apply world gravity.
 | 
	
		
			
				|  |  | -        // TODO - Why is the 4x scalar necessary.
 | 
	
		
			
				|  |  | -        velocity.add(this.system.world.gravity.scale(dt * 4.0 / 1000));
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | +function isNullVector (vector) {
 | 
	
		
			
				|  |  | +  return vector.x === 0 && vector.y === 0 && vector.z === 0;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      // 6. If the ground surface has a velocity, apply it directly to current
 | 
	
		
			
				|  |  | -      // position, not velocity, to preserve relative velocity.
 | 
	
		
			
				|  |  | -      if (groundBody && groundBody.el && groundBody.el.components.velocity) {
 | 
	
		
			
				|  |  | -        var groundVelocity = groundBody.el.getAttribute('velocity');
 | 
	
		
			
				|  |  | -        body.position.copy({
 | 
	
		
			
				|  |  | -          x: body.position.x + groundVelocity.x * dt / 1000,
 | 
	
		
			
				|  |  | -          y: body.position.y + groundVelocity.y * dt / 1000,
 | 
	
		
			
				|  |  | -          z: body.position.z + groundVelocity.z * dt / 1000
 | 
	
		
			
				|  |  | -        });
 | 
	
		
			
				|  |  | -        this.el.setAttribute('position', body.position);
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | +},{}],89:[function(require,module,exports){
 | 
	
		
			
				|  |  | +var physics = require('aframe-physics-system');
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      body.velocity.copy(velocity);
 | 
	
		
			
				|  |  | -      this.el.setAttribute('velocity', velocity);
 | 
	
		
			
				|  |  | -    };
 | 
	
		
			
				|  |  | -  }()),
 | 
	
		
			
				|  |  | +module.exports = {
 | 
	
		
			
				|  |  | +  'checkpoint-controls': require('./checkpoint-controls'),
 | 
	
		
			
				|  |  | +  'gamepad-controls':    require('./gamepad-controls'),
 | 
	
		
			
				|  |  | +  'hmd-controls':        require('./hmd-controls'),
 | 
	
		
			
				|  |  | +  'keyboard-controls':   require('./keyboard-controls'),
 | 
	
		
			
				|  |  | +  'mouse-controls':      require('./mouse-controls'),
 | 
	
		
			
				|  |  | +  'touch-controls':      require('./touch-controls'),
 | 
	
		
			
				|  |  | +  'universal-controls':  require('./universal-controls'),
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /**
 | 
	
		
			
				|  |  | -   * When walking on complex surfaces (trimeshes, borders between two shapes),
 | 
	
		
			
				|  |  | -   * the collision normals returned for the player sphere can be very
 | 
	
		
			
				|  |  | -   * inconsistent. To address this, raycast straight down, find the collision
 | 
	
		
			
				|  |  | -   * normal, and return whichever normal is more vertical.
 | 
	
		
			
				|  |  | -   * @param  {CANNON.Body} groundBody
 | 
	
		
			
				|  |  | -   * @param  {CANNON.Vec3} groundNormal
 | 
	
		
			
				|  |  | -   * @return {CANNON.Vec3}
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | -  raycastToGround: function (groundBody, groundNormal) {
 | 
	
		
			
				|  |  | -    var ray,
 | 
	
		
			
				|  |  | -        hitNormal,
 | 
	
		
			
				|  |  | -        vFrom = this.body.position,
 | 
	
		
			
				|  |  | -        vTo = this.body.position.clone();
 | 
	
		
			
				|  |  | +  registerAll: function (AFRAME) {
 | 
	
		
			
				|  |  | +    if (this._registered) return;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    vTo.y -= this.data.height;
 | 
	
		
			
				|  |  | -    ray = new CANNON.Ray(vFrom, vTo);
 | 
	
		
			
				|  |  | -    ray._updateDirection(); // TODO - Report bug.
 | 
	
		
			
				|  |  | -    ray.intersectBody(groundBody);
 | 
	
		
			
				|  |  | +    AFRAME = AFRAME || window.AFRAME;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (!ray.hasHit) return groundNormal;
 | 
	
		
			
				|  |  | +    physics.registerAll();
 | 
	
		
			
				|  |  | +    if (!AFRAME.components['checkpoint-controls'])  AFRAME.registerComponent('checkpoint-controls', this['checkpoint-controls']);
 | 
	
		
			
				|  |  | +    if (!AFRAME.components['gamepad-controls'])     AFRAME.registerComponent('gamepad-controls',    this['gamepad-controls']);
 | 
	
		
			
				|  |  | +    if (!AFRAME.components['hmd-controls'])         AFRAME.registerComponent('hmd-controls',        this['hmd-controls']);
 | 
	
		
			
				|  |  | +    if (!AFRAME.components['keyboard-controls'])    AFRAME.registerComponent('keyboard-controls',   this['keyboard-controls']);
 | 
	
		
			
				|  |  | +    if (!AFRAME.components['mouse-controls'])       AFRAME.registerComponent('mouse-controls',      this['mouse-controls']);
 | 
	
		
			
				|  |  | +    if (!AFRAME.components['touch-controls'])       AFRAME.registerComponent('touch-controls',      this['touch-controls']);
 | 
	
		
			
				|  |  | +    if (!AFRAME.components['universal-controls'])   AFRAME.registerComponent('universal-controls',  this['universal-controls']);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // Compare ABS, in case we're projecting against the inside of the face.
 | 
	
		
			
				|  |  | -    hitNormal = ray.result.hitNormalWorld;
 | 
	
		
			
				|  |  | -    return Math.abs(hitNormal.y) > Math.abs(groundNormal.y) ? hitNormal : groundNormal;
 | 
	
		
			
				|  |  | +    this._registered = true;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{}],104:[function(require,module,exports){
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * Apply this component to models that looks "blocky", to have Three.js compute
 | 
	
		
			
				|  |  | - * vertex normals on the fly for a "smoother" look.
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -module.exports = {
 | 
	
		
			
				|  |  | -  init: function () {
 | 
	
		
			
				|  |  | -    this.el.addEventListener('model-loaded', function (e) {
 | 
	
		
			
				|  |  | -      e.detail.model.traverse(function (node) {
 | 
	
		
			
				|  |  | -        if (node.isMesh) node.geometry.computeVertexNormals();
 | 
	
		
			
				|  |  | -      });
 | 
	
		
			
				|  |  | -    })
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | +},{"./checkpoint-controls":86,"./gamepad-controls":87,"./hmd-controls":88,"./keyboard-controls":90,"./mouse-controls":91,"./touch-controls":92,"./universal-controls":93,"aframe-physics-system":11}],90:[function(require,module,exports){
 | 
	
		
			
				|  |  | +require('../../lib/keyboard.polyfill');
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +var MAX_DELTA = 0.2,
 | 
	
		
			
				|  |  | +    PROXY_FLAG = '__keyboard-controls-proxy';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +var KeyboardEvent = window.KeyboardEvent;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{}],105:[function(require,module,exports){
 | 
	
		
			
				|  |  |  /**
 | 
	
		
			
				|  |  | - * Based on aframe/examples/showcase/tracked-controls.
 | 
	
		
			
				|  |  | + * Keyboard Controls component.
 | 
	
		
			
				|  |  |   *
 | 
	
		
			
				|  |  | - * Implement bounding sphere collision detection for entities with a mesh.
 | 
	
		
			
				|  |  | - * Sets the specified state on the intersected entities.
 | 
	
		
			
				|  |  | + * Stripped-down version of: https://github.com/donmccurdy/aframe-keyboard-controls
 | 
	
		
			
				|  |  |   *
 | 
	
		
			
				|  |  | - * @property {string} objects - Selector of the entities to test for collision.
 | 
	
		
			
				|  |  | - * @property {string} state - State to set on collided entities.
 | 
	
		
			
				|  |  | + * Bind keyboard events to components, or control your entities with the WASD keys.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Why use KeyboardEvent.code? "This is set to a string representing the key that was pressed to
 | 
	
		
			
				|  |  | + * generate the KeyboardEvent, without taking the current keyboard layout (e.g., QWERTY vs.
 | 
	
		
			
				|  |  | + * Dvorak), locale (e.g., English vs. French), or any modifier keys into account. This is useful
 | 
	
		
			
				|  |  | + * when you care about which physical key was pressed, rather thanwhich character it corresponds
 | 
	
		
			
				|  |  | + * to. For example, if you’re a writing a game, you might want a certain set of keys to move the
 | 
	
		
			
				|  |  | + * player in different directions, and that mapping should ideally be independent of keyboard
 | 
	
		
			
				|  |  | + * layout. See: https://developers.google.com/web/updates/2016/04/keyboardevent-keys-codes
 | 
	
		
			
				|  |  |   *
 | 
	
		
			
				|  |  | + * @namespace wasd-controls
 | 
	
		
			
				|  |  | + * keys the entity moves and if you release it will stop. Easing simulates friction.
 | 
	
		
			
				|  |  | + * to the entity when pressing the keys.
 | 
	
		
			
				|  |  | + * @param {bool} [enabled=true] - To completely enable or disable the controls
 | 
	
		
			
				|  |  |   */
 | 
	
		
			
				|  |  |  module.exports = {
 | 
	
		
			
				|  |  |    schema: {
 | 
	
		
			
				|  |  | -    objects: {default: ''},
 | 
	
		
			
				|  |  | -    state: {default: 'collided'},
 | 
	
		
			
				|  |  | -    radius: {default: 0.05},
 | 
	
		
			
				|  |  | -    watch: {default: true}
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  init: function () {
 | 
	
		
			
				|  |  | -    /** @type {MutationObserver} */
 | 
	
		
			
				|  |  | -    this.observer = null;
 | 
	
		
			
				|  |  | -    /** @type {Array<Element>} Elements to watch for collisions. */
 | 
	
		
			
				|  |  | -    this.els = [];
 | 
	
		
			
				|  |  | -    /** @type {Array<Element>} Elements currently in collision state. */
 | 
	
		
			
				|  |  | -    this.collisions = [];
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    this.handleHit = this.handleHit.bind(this);
 | 
	
		
			
				|  |  | +    enabled:           { default: true },
 | 
	
		
			
				|  |  | +    debug:             { default: false }
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  remove: function () {
 | 
	
		
			
				|  |  | -    this.pause();
 | 
	
		
			
				|  |  | +  init: function () {
 | 
	
		
			
				|  |  | +    this.dVelocity = new THREE.Vector3();
 | 
	
		
			
				|  |  | +    this.localKeys = {};
 | 
	
		
			
				|  |  | +    this.listeners = {
 | 
	
		
			
				|  |  | +      keydown: this.onKeyDown.bind(this),
 | 
	
		
			
				|  |  | +      keyup: this.onKeyUp.bind(this),
 | 
	
		
			
				|  |  | +      blur: this.onBlur.bind(this)
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +    this.attachEventListeners();
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  play: function () {
 | 
	
		
			
				|  |  | -    var sceneEl = this.el.sceneEl;
 | 
	
		
			
				|  |  | +  /*******************************************************************
 | 
	
		
			
				|  |  | +  * Movement
 | 
	
		
			
				|  |  | +  */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (this.data.watch) {
 | 
	
		
			
				|  |  | -      this.observer = new MutationObserver(this.update.bind(this, null));
 | 
	
		
			
				|  |  | -      this.observer.observe(sceneEl, {childList: true, subtree: true});
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +  isVelocityActive: function () {
 | 
	
		
			
				|  |  | +    return this.data.enabled && !!Object.keys(this.getKeys()).length;
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  pause: function () {
 | 
	
		
			
				|  |  | -    if (this.observer) {
 | 
	
		
			
				|  |  | -      this.observer.disconnect();
 | 
	
		
			
				|  |  | -      this.observer = null;
 | 
	
		
			
				|  |  | +  getVelocityDelta: function () {
 | 
	
		
			
				|  |  | +    var data = this.data,
 | 
	
		
			
				|  |  | +        keys = this.getKeys();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    this.dVelocity.set(0, 0, 0);
 | 
	
		
			
				|  |  | +    if (data.enabled) {
 | 
	
		
			
				|  |  | +      if (keys.KeyW || keys.ArrowUp)    { this.dVelocity.z -= 1; }
 | 
	
		
			
				|  |  | +      if (keys.KeyA || keys.ArrowLeft)  { this.dVelocity.x -= 1; }
 | 
	
		
			
				|  |  | +      if (keys.KeyS || keys.ArrowDown)  { this.dVelocity.z += 1; }
 | 
	
		
			
				|  |  | +      if (keys.KeyD || keys.ArrowRight) { this.dVelocity.x += 1; }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    return this.dVelocity.clone();
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /**
 | 
	
		
			
				|  |  | -   * Update list of entities to test for collision.
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | -  update: function () {
 | 
	
		
			
				|  |  | -    var data = this.data;
 | 
	
		
			
				|  |  | -    var objectEls;
 | 
	
		
			
				|  |  | +  /*******************************************************************
 | 
	
		
			
				|  |  | +  * Events
 | 
	
		
			
				|  |  | +  */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // Push entities into list of els to intersect.
 | 
	
		
			
				|  |  | -    if (data.objects) {
 | 
	
		
			
				|  |  | -      objectEls = this.el.sceneEl.querySelectorAll(data.objects);
 | 
	
		
			
				|  |  | -    } else {
 | 
	
		
			
				|  |  | -      // If objects not defined, intersect with everything.
 | 
	
		
			
				|  |  | -      objectEls = this.el.sceneEl.children;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    // Convert from NodeList to Array
 | 
	
		
			
				|  |  | -    this.els = Array.prototype.slice.call(objectEls);
 | 
	
		
			
				|  |  | +  play: function () {
 | 
	
		
			
				|  |  | +    this.attachEventListeners();
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  tick: (function () {
 | 
	
		
			
				|  |  | -    var position = new THREE.Vector3(),
 | 
	
		
			
				|  |  | -        meshPosition = new THREE.Vector3(),
 | 
	
		
			
				|  |  | -        meshScale = new THREE.Vector3(),
 | 
	
		
			
				|  |  | -        colliderScale = new THREE.Vector3(),
 | 
	
		
			
				|  |  | -        distanceMap = new Map();
 | 
	
		
			
				|  |  | -    return function () {
 | 
	
		
			
				|  |  | -      var el = this.el,
 | 
	
		
			
				|  |  | -          data = this.data,
 | 
	
		
			
				|  |  | -          mesh = el.getObject3D('mesh'),
 | 
	
		
			
				|  |  | -          colliderRadius,
 | 
	
		
			
				|  |  | -          collisions = [];
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      if (!mesh) { return; }
 | 
	
		
			
				|  |  | +  pause: function () {
 | 
	
		
			
				|  |  | +    this.removeEventListeners();
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      distanceMap.clear();
 | 
	
		
			
				|  |  | -      position.copy(el.object3D.getWorldPosition());
 | 
	
		
			
				|  |  | -      el.object3D.getWorldScale(colliderScale);
 | 
	
		
			
				|  |  | -      colliderRadius = data.radius * scaleFactor(colliderScale);
 | 
	
		
			
				|  |  | -      // Update collision list.
 | 
	
		
			
				|  |  | -      this.els.forEach(intersect);
 | 
	
		
			
				|  |  | +  remove: function () {
 | 
	
		
			
				|  |  | +    this.pause();
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      // Emit events and add collision states, in order of distance.
 | 
	
		
			
				|  |  | -      collisions
 | 
	
		
			
				|  |  | -        .sort(function (a, b) {
 | 
	
		
			
				|  |  | -          return distanceMap.get(a) > distanceMap.get(b) ? 1 : -1;
 | 
	
		
			
				|  |  | -        })
 | 
	
		
			
				|  |  | -        .forEach(this.handleHit);
 | 
	
		
			
				|  |  | +  attachEventListeners: function () {
 | 
	
		
			
				|  |  | +    window.addEventListener('keydown', this.listeners.keydown, false);
 | 
	
		
			
				|  |  | +    window.addEventListener('keyup', this.listeners.keyup, false);
 | 
	
		
			
				|  |  | +    window.addEventListener('blur', this.listeners.blur, false);
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      // Remove collision state from current element.
 | 
	
		
			
				|  |  | -      if (collisions.length === 0) { el.emit('hit', {el: null}); }
 | 
	
		
			
				|  |  | +  removeEventListeners: function () {
 | 
	
		
			
				|  |  | +    window.removeEventListener('keydown', this.listeners.keydown);
 | 
	
		
			
				|  |  | +    window.removeEventListener('keyup', this.listeners.keyup);
 | 
	
		
			
				|  |  | +    window.removeEventListener('blur', this.listeners.blur);
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      // Remove collision state from other elements.
 | 
	
		
			
				|  |  | -      this.collisions.filter(function (el) {
 | 
	
		
			
				|  |  | -        return !distanceMap.has(el);
 | 
	
		
			
				|  |  | -      }).forEach(function removeState (el) {
 | 
	
		
			
				|  |  | -        el.removeState(data.state);
 | 
	
		
			
				|  |  | -      });
 | 
	
		
			
				|  |  | +  onKeyDown: function (event) {
 | 
	
		
			
				|  |  | +    if (AFRAME.utils.shouldCaptureKeyEvent(event)) {
 | 
	
		
			
				|  |  | +      this.localKeys[event.code] = true;
 | 
	
		
			
				|  |  | +      this.emit(event);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      // Store new collisions
 | 
	
		
			
				|  |  | -      this.collisions = collisions;
 | 
	
		
			
				|  |  | +  onKeyUp: function (event) {
 | 
	
		
			
				|  |  | +    if (AFRAME.utils.shouldCaptureKeyEvent(event)) {
 | 
	
		
			
				|  |  | +      delete this.localKeys[event.code];
 | 
	
		
			
				|  |  | +      this.emit(event);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      // Bounding sphere collision detection
 | 
	
		
			
				|  |  | -      function intersect (el) {
 | 
	
		
			
				|  |  | -        var radius, mesh, distance, box, extent, size;
 | 
	
		
			
				|  |  | +  onBlur: function () {
 | 
	
		
			
				|  |  | +    for (var code in this.localKeys) {
 | 
	
		
			
				|  |  | +      if (this.localKeys.hasOwnProperty(code)) {
 | 
	
		
			
				|  |  | +        delete this.localKeys[code];
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        if (!el.isEntity) { return; }
 | 
	
		
			
				|  |  | +  emit: function (event) {
 | 
	
		
			
				|  |  | +    // TODO - keydown only initially?
 | 
	
		
			
				|  |  | +    // TODO - where the f is the spacebar
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        mesh = el.getObject3D('mesh');
 | 
	
		
			
				|  |  | +    // Emit original event.
 | 
	
		
			
				|  |  | +    if (PROXY_FLAG in event) {
 | 
	
		
			
				|  |  | +      // TODO - Method never triggered.
 | 
	
		
			
				|  |  | +      this.el.emit(event.type, event);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        if (!mesh) { return; }
 | 
	
		
			
				|  |  | +    // Emit convenience event, identifying key.
 | 
	
		
			
				|  |  | +    this.el.emit(event.type + ':' + event.code, new KeyboardEvent(event.type, event));
 | 
	
		
			
				|  |  | +    if (this.data.debug) console.log(event.type + ':' + event.code);
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        box = new THREE.Box3().setFromObject(mesh);
 | 
	
		
			
				|  |  | -        size = box.getSize();
 | 
	
		
			
				|  |  | -        extent = Math.max(size.x, size.y, size.z) / 2;
 | 
	
		
			
				|  |  | -        radius = Math.sqrt(2 * extent * extent);
 | 
	
		
			
				|  |  | -        box.getCenter(meshPosition);
 | 
	
		
			
				|  |  | +  /*******************************************************************
 | 
	
		
			
				|  |  | +  * Accessors
 | 
	
		
			
				|  |  | +  */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        if (!radius) { return; }
 | 
	
		
			
				|  |  | +  isPressed: function (code) {
 | 
	
		
			
				|  |  | +    return code in this.getKeys();
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        distance = position.distanceTo(meshPosition);
 | 
	
		
			
				|  |  | -        if (distance < radius + colliderRadius) {
 | 
	
		
			
				|  |  | -          collisions.push(el);
 | 
	
		
			
				|  |  | -          distanceMap.set(el, distance);
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -      // use max of scale factors to maintain bounding sphere collision
 | 
	
		
			
				|  |  | -      function scaleFactor (scaleVec) {
 | 
	
		
			
				|  |  | -        return Math.max.apply(null, scaleVec.toArray());
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -    };
 | 
	
		
			
				|  |  | -  })(),
 | 
	
		
			
				|  |  | +  getKeys: function () {
 | 
	
		
			
				|  |  | +    if (this.isProxied()) {
 | 
	
		
			
				|  |  | +      return this.el.sceneEl.components['proxy-controls'].getKeyboard();
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    return this.localKeys;
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  handleHit: function (targetEl) {
 | 
	
		
			
				|  |  | -    targetEl.emit('hit');
 | 
	
		
			
				|  |  | -    targetEl.addState(this.data.state);
 | 
	
		
			
				|  |  | -    this.el.emit('hit', {el: targetEl});
 | 
	
		
			
				|  |  | +  isProxied: function () {
 | 
	
		
			
				|  |  | +    var proxyControls = this.el.sceneEl.components['proxy-controls'];
 | 
	
		
			
				|  |  | +    return proxyControls && proxyControls.isConnected();
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{}],106:[function(require,module,exports){
 | 
	
		
			
				|  |  | +},{"../../lib/keyboard.polyfill":10}],91:[function(require,module,exports){
 | 
	
		
			
				|  |  | +document.exitPointerLock = document.exitPointerLock || document.mozExitPointerLock;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  /**
 | 
	
		
			
				|  |  | - * Toggle velocity.
 | 
	
		
			
				|  |  | + * Mouse + Pointerlock controls.
 | 
	
		
			
				|  |  |   *
 | 
	
		
			
				|  |  | - * Moves an object back and forth along an axis, within a min/max extent.
 | 
	
		
			
				|  |  | + * Based on: https://github.com/aframevr/aframe/pull/1056
 | 
	
		
			
				|  |  |   */
 | 
	
		
			
				|  |  |  module.exports = {
 | 
	
		
			
				|  |  | -  dependencies: ['velocity'],
 | 
	
		
			
				|  |  |    schema: {
 | 
	
		
			
				|  |  | -    axis: { default: 'x', oneOf: ['x', 'y', 'z'] },
 | 
	
		
			
				|  |  | -    min: { default: 0 },
 | 
	
		
			
				|  |  | -    max: { default: 0 },
 | 
	
		
			
				|  |  | -    speed: { default: 1 }
 | 
	
		
			
				|  |  | +    enabled: { default: true },
 | 
	
		
			
				|  |  | +    pointerlockEnabled: { default: true },
 | 
	
		
			
				|  |  | +    sensitivity: { default: 1 / 25 }
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  | -  init: function () {
 | 
	
		
			
				|  |  | -    var velocity = {x: 0, y: 0, z: 0};
 | 
	
		
			
				|  |  | -    velocity[this.data.axis] = this.data.speed;
 | 
	
		
			
				|  |  | -    this.el.setAttribute('velocity', velocity);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (this.el.sceneEl.addBehavior) this.el.sceneEl.addBehavior(this);
 | 
	
		
			
				|  |  | +  init: function () {
 | 
	
		
			
				|  |  | +    this.mouseDown = false;
 | 
	
		
			
				|  |  | +    this.pointerLocked = false;
 | 
	
		
			
				|  |  | +    this.lookVector = new THREE.Vector2();
 | 
	
		
			
				|  |  | +    this.bindMethods();
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  | -  remove: function () {},
 | 
	
		
			
				|  |  | -  update: function () { this.tick(); },
 | 
	
		
			
				|  |  | -  tick: function () {
 | 
	
		
			
				|  |  | -    var data = this.data,
 | 
	
		
			
				|  |  | -        velocity = this.el.getAttribute('velocity'),
 | 
	
		
			
				|  |  | -        position = this.el.getAttribute('position');
 | 
	
		
			
				|  |  | -    if (velocity[data.axis] > 0 && position[data.axis] > data.max) {
 | 
	
		
			
				|  |  | -      velocity[data.axis] = -data.speed;
 | 
	
		
			
				|  |  | -      this.el.setAttribute('velocity', velocity);
 | 
	
		
			
				|  |  | -    } else if (velocity[data.axis] < 0 && position[data.axis] < data.min) {
 | 
	
		
			
				|  |  | -      velocity[data.axis] = data.speed;
 | 
	
		
			
				|  |  | -      this.el.setAttribute('velocity', velocity);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  update: function (previousData) {
 | 
	
		
			
				|  |  | +    var data = this.data;
 | 
	
		
			
				|  |  | +    if (previousData.pointerlockEnabled && !data.pointerlockEnabled && this.pointerLocked) {
 | 
	
		
			
				|  |  | +      document.exitPointerLock();
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{}],107:[function(require,module,exports){
 | 
	
		
			
				|  |  | -module.exports = {
 | 
	
		
			
				|  |  | -  'nav-mesh':    require('./nav-mesh'),
 | 
	
		
			
				|  |  | -  'nav-controller':     require('./nav-controller'),
 | 
	
		
			
				|  |  | -  'system':      require('./system'),
 | 
	
		
			
				|  |  | +  play: function () {
 | 
	
		
			
				|  |  | +    this.addEventListeners();
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  registerAll: function (AFRAME) {
 | 
	
		
			
				|  |  | -    if (this._registered) return;
 | 
	
		
			
				|  |  | +  pause: function () {
 | 
	
		
			
				|  |  | +    this.removeEventListeners();
 | 
	
		
			
				|  |  | +    this.lookVector.set(0, 0);
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    AFRAME = AFRAME || window.AFRAME;
 | 
	
		
			
				|  |  | +  remove: function () {
 | 
	
		
			
				|  |  | +    this.pause();
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (!AFRAME.components['nav-mesh']) {
 | 
	
		
			
				|  |  | -      AFRAME.registerComponent('nav-mesh', this['nav-mesh']);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +  bindMethods: function () {
 | 
	
		
			
				|  |  | +    this.onMouseDown = this.onMouseDown.bind(this);
 | 
	
		
			
				|  |  | +    this.onMouseMove = this.onMouseMove.bind(this);
 | 
	
		
			
				|  |  | +    this.onMouseUp = this.onMouseUp.bind(this);
 | 
	
		
			
				|  |  | +    this.onMouseUp = this.onMouseUp.bind(this);
 | 
	
		
			
				|  |  | +    this.onPointerLockChange = this.onPointerLockChange.bind(this);
 | 
	
		
			
				|  |  | +    this.onPointerLockChange = this.onPointerLockChange.bind(this);
 | 
	
		
			
				|  |  | +    this.onPointerLockChange = this.onPointerLockChange.bind(this);
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (!AFRAME.components['nav-controller']) {
 | 
	
		
			
				|  |  | -      AFRAME.registerComponent('nav-controller',  this['nav-controller']);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +  addEventListeners: function () {
 | 
	
		
			
				|  |  | +    var sceneEl = this.el.sceneEl;
 | 
	
		
			
				|  |  | +    var canvasEl = sceneEl.canvas;
 | 
	
		
			
				|  |  | +    var data = this.data;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (!AFRAME.systems.nav) {
 | 
	
		
			
				|  |  | -      AFRAME.registerSystem('nav', this.system);
 | 
	
		
			
				|  |  | +    if (!canvasEl) {
 | 
	
		
			
				|  |  | +      sceneEl.addEventListener('render-target-loaded', this.addEventListeners.bind(this));
 | 
	
		
			
				|  |  | +      return;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    this._registered = true;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | +    canvasEl.addEventListener('mousedown', this.onMouseDown, false);
 | 
	
		
			
				|  |  | +    canvasEl.addEventListener('mousemove', this.onMouseMove, false);
 | 
	
		
			
				|  |  | +    canvasEl.addEventListener('mouseup', this.onMouseUp, false);
 | 
	
		
			
				|  |  | +    canvasEl.addEventListener('mouseout', this.onMouseUp, false);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{"./nav-controller":108,"./nav-mesh":109,"./system":110}],108:[function(require,module,exports){
 | 
	
		
			
				|  |  | -module.exports = {
 | 
	
		
			
				|  |  | -  schema: {
 | 
	
		
			
				|  |  | -    destination: {type: 'vec3'},
 | 
	
		
			
				|  |  | -    active: {default: false},
 | 
	
		
			
				|  |  | -    speed: {default: 2}
 | 
	
		
			
				|  |  | +    if (data.pointerlockEnabled) {
 | 
	
		
			
				|  |  | +      document.addEventListener('pointerlockchange', this.onPointerLockChange, false);
 | 
	
		
			
				|  |  | +      document.addEventListener('mozpointerlockchange', this.onPointerLockChange, false);
 | 
	
		
			
				|  |  | +      document.addEventListener('pointerlockerror', this.onPointerLockError, false);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  | -  init: function () {
 | 
	
		
			
				|  |  | -    this.system = this.el.sceneEl.systems.nav;
 | 
	
		
			
				|  |  | -    this.system.addController(this);
 | 
	
		
			
				|  |  | -    this.path = [];
 | 
	
		
			
				|  |  | -    this.raycaster = new THREE.Raycaster();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  removeEventListeners: function () {
 | 
	
		
			
				|  |  | +    var canvasEl = this.el.sceneEl && this.el.sceneEl.canvas;
 | 
	
		
			
				|  |  | +    if (canvasEl) {
 | 
	
		
			
				|  |  | +      canvasEl.removeEventListener('mousedown', this.onMouseDown, false);
 | 
	
		
			
				|  |  | +      canvasEl.removeEventListener('mousemove', this.onMouseMove, false);
 | 
	
		
			
				|  |  | +      canvasEl.removeEventListener('mouseup', this.onMouseUp, false);
 | 
	
		
			
				|  |  | +      canvasEl.removeEventListener('mouseout', this.onMouseUp, false);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    document.removeEventListener('pointerlockchange', this.onPointerLockChange, false);
 | 
	
		
			
				|  |  | +    document.removeEventListener('mozpointerlockchange', this.onPointerLockChange, false);
 | 
	
		
			
				|  |  | +    document.removeEventListener('pointerlockerror', this.onPointerLockError, false);
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  | -  remove: function () {
 | 
	
		
			
				|  |  | -    this.system.removeController(this);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  isRotationActive: function () {
 | 
	
		
			
				|  |  | +    return this.data.enabled && (this.mouseDown || this.pointerLocked);
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  | -  update: function () {
 | 
	
		
			
				|  |  | -    this.path.length = 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * Returns the sum of all mouse movement since last call.
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  getRotationDelta: function () {
 | 
	
		
			
				|  |  | +    var dRotation = this.lookVector.clone().multiplyScalar(this.data.sensitivity);
 | 
	
		
			
				|  |  | +    this.lookVector.set(0, 0);
 | 
	
		
			
				|  |  | +    return dRotation;
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  | -  tick: (function () {
 | 
	
		
			
				|  |  | -    var vDest = new THREE.Vector3();
 | 
	
		
			
				|  |  | -    var vDelta = new THREE.Vector3();
 | 
	
		
			
				|  |  | -    var vNext = new THREE.Vector3();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    return function (t, dt) {
 | 
	
		
			
				|  |  | -      var el = this.el;
 | 
	
		
			
				|  |  | -      var data = this.data;
 | 
	
		
			
				|  |  | -      var raycaster = this.raycaster;
 | 
	
		
			
				|  |  | -      var speed = data.speed * dt / 1000;
 | 
	
		
			
				|  |  | +  onMouseMove: function (event) {
 | 
	
		
			
				|  |  | +    var previousMouseEvent = this.previousMouseEvent;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      if (!data.active) return;
 | 
	
		
			
				|  |  | +    if (!this.data.enabled || !(this.mouseDown || this.pointerLocked)) {
 | 
	
		
			
				|  |  | +      return;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      // Use PatrolJS pathfinding system to get shortest path to target.
 | 
	
		
			
				|  |  | -      if (!this.path.length) {
 | 
	
		
			
				|  |  | -        this.path = this.system.getPath(this.el.object3D, vDest.copy(data.destination));
 | 
	
		
			
				|  |  | -        this.path = this.path || [];
 | 
	
		
			
				|  |  | -        el.emit('nav-start');
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | +    var movementX = event.movementX || event.mozMovementX || 0;
 | 
	
		
			
				|  |  | +    var movementY = event.movementY || event.mozMovementY || 0;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      // If no path is found, exit.
 | 
	
		
			
				|  |  | -      if (!this.path.length) {
 | 
	
		
			
				|  |  | -        console.warn('[nav] Unable to find path to %o.', data.destination);
 | 
	
		
			
				|  |  | -        this.el.setAttribute('nav-controller', {active: false});
 | 
	
		
			
				|  |  | -        el.emit('nav-end');
 | 
	
		
			
				|  |  | -        return;
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | +    if (!this.pointerLocked) {
 | 
	
		
			
				|  |  | +      movementX = event.screenX - previousMouseEvent.screenX;
 | 
	
		
			
				|  |  | +      movementY = event.screenY - previousMouseEvent.screenY;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      // Current segment is a vector from current position to next waypoint.
 | 
	
		
			
				|  |  | -      var vCurrent = el.object3D.position;
 | 
	
		
			
				|  |  | -      var vWaypoint = this.path[0];
 | 
	
		
			
				|  |  | -      vDelta.subVectors(vWaypoint, vCurrent);
 | 
	
		
			
				|  |  | +    this.lookVector.x += movementX;
 | 
	
		
			
				|  |  | +    this.lookVector.y += movementY;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      var distance = vDelta.length();
 | 
	
		
			
				|  |  | -      var gazeTarget;
 | 
	
		
			
				|  |  | +    this.previousMouseEvent = event;
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      if (distance < speed) {
 | 
	
		
			
				|  |  | -        // If <1 step from current waypoint, discard it and move toward next.
 | 
	
		
			
				|  |  | -        this.path.shift();
 | 
	
		
			
				|  |  | +  onMouseDown: function (event) {
 | 
	
		
			
				|  |  | +    var canvasEl = this.el.sceneEl.canvas,
 | 
	
		
			
				|  |  | +        isEditing = (AFRAME.INSPECTOR || {}).opened;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        // After discarding the last waypoint, exit pathfinding.
 | 
	
		
			
				|  |  | -        if (!this.path.length) {
 | 
	
		
			
				|  |  | -          this.el.setAttribute('nav-controller', {active: false});
 | 
	
		
			
				|  |  | -          el.emit('nav-end');
 | 
	
		
			
				|  |  | -          return;
 | 
	
		
			
				|  |  | -        } else {
 | 
	
		
			
				|  |  | -          gazeTarget = this.path[0];
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -      } else {
 | 
	
		
			
				|  |  | -        // If still far away from next waypoint, find next position for
 | 
	
		
			
				|  |  | -        // the current frame.
 | 
	
		
			
				|  |  | -        vNext.copy(vDelta.setLength(speed)).add(vCurrent);
 | 
	
		
			
				|  |  | -        gazeTarget = vWaypoint;
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | +    this.mouseDown = true;
 | 
	
		
			
				|  |  | +    this.previousMouseEvent = event;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      // Look at the next waypoint.
 | 
	
		
			
				|  |  | -      gazeTarget.y = vCurrent.y;
 | 
	
		
			
				|  |  | -      el.object3D.lookAt(gazeTarget);
 | 
	
		
			
				|  |  | +    if (this.data.pointerlockEnabled && !this.pointerLocked && !isEditing) {
 | 
	
		
			
				|  |  | +      if (canvasEl.requestPointerLock) {
 | 
	
		
			
				|  |  | +        canvasEl.requestPointerLock();
 | 
	
		
			
				|  |  | +      } else if (canvasEl.mozRequestPointerLock) {
 | 
	
		
			
				|  |  | +        canvasEl.mozRequestPointerLock();
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      // Raycast against the nav mesh, to keep the controller moving along the
 | 
	
		
			
				|  |  | -      // ground, not traveling in a straight line from higher to lower waypoints.
 | 
	
		
			
				|  |  | -      raycaster.ray.origin.copy(vNext);
 | 
	
		
			
				|  |  | -      raycaster.ray.origin.y += 1.5;
 | 
	
		
			
				|  |  | -      raycaster.ray.direction.y = -1;
 | 
	
		
			
				|  |  | -      var intersections = raycaster.intersectObject(this.system.getNavMesh());
 | 
	
		
			
				|  |  | +  onMouseUp: function () {
 | 
	
		
			
				|  |  | +    this.mouseDown = false;
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      if (!intersections.length) {
 | 
	
		
			
				|  |  | -        // Raycasting failed. Step toward the waypoint and hope for the best.
 | 
	
		
			
				|  |  | -        vCurrent.copy(vNext);
 | 
	
		
			
				|  |  | -      } else {
 | 
	
		
			
				|  |  | -        // Re-project next position onto nav mesh.
 | 
	
		
			
				|  |  | -        vDelta.subVectors(intersections[0].point, vCurrent);
 | 
	
		
			
				|  |  | -        vCurrent.add(vDelta.setLength(speed));
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | +  onPointerLockChange: function () {
 | 
	
		
			
				|  |  | +    this.pointerLocked = !!(document.pointerLockElement || document.mozPointerLockElement);
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    };
 | 
	
		
			
				|  |  | -  }())
 | 
	
		
			
				|  |  | +  onPointerLockError: function () {
 | 
	
		
			
				|  |  | +    this.pointerLocked = false;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{}],109:[function(require,module,exports){
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * nav-mesh
 | 
	
		
			
				|  |  | - *
 | 
	
		
			
				|  |  | - * Waits for a mesh to be loaded on the current entity, then sets it as the
 | 
	
		
			
				|  |  | - * nav mesh in the pathfinding system.
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | +},{}],92:[function(require,module,exports){
 | 
	
		
			
				|  |  |  module.exports = {
 | 
	
		
			
				|  |  | +  schema: {
 | 
	
		
			
				|  |  | +    enabled: { default: true }
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    init: function () {
 | 
	
		
			
				|  |  | -    this.system = this.el.sceneEl.systems.nav;
 | 
	
		
			
				|  |  | -    this.loadNavMesh();
 | 
	
		
			
				|  |  | -    this.el.addEventListener('model-loaded', this.loadNavMesh.bind(this));
 | 
	
		
			
				|  |  | +    this.dVelocity = new THREE.Vector3();
 | 
	
		
			
				|  |  | +    this.bindMethods();
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  loadNavMesh: function () {
 | 
	
		
			
				|  |  | -    var object = this.el.getObject3D('mesh');
 | 
	
		
			
				|  |  | +  play: function () {
 | 
	
		
			
				|  |  | +    this.addEventListeners();
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (!object) return;
 | 
	
		
			
				|  |  | +  pause: function () {
 | 
	
		
			
				|  |  | +    this.removeEventListeners();
 | 
	
		
			
				|  |  | +    this.dVelocity.set(0, 0, 0);
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    var navMesh;
 | 
	
		
			
				|  |  | -    object.traverse(function (node) {
 | 
	
		
			
				|  |  | -      if (node.isMesh) navMesh = node;
 | 
	
		
			
				|  |  | -    });
 | 
	
		
			
				|  |  | +  remove: function () {
 | 
	
		
			
				|  |  | +    this.pause();
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (!navMesh) return;
 | 
	
		
			
				|  |  | +  addEventListeners: function () {
 | 
	
		
			
				|  |  | +    var sceneEl = this.el.sceneEl;
 | 
	
		
			
				|  |  | +    var canvasEl = sceneEl.canvas;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    this.system.setNavMesh(navMesh);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | +    if (!canvasEl) {
 | 
	
		
			
				|  |  | +      sceneEl.addEventListener('render-target-loaded', this.addEventListeners.bind(this));
 | 
	
		
			
				|  |  | +      return;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{}],110:[function(require,module,exports){
 | 
	
		
			
				|  |  | -var Path = require('three-pathfinding');
 | 
	
		
			
				|  |  | +    canvasEl.addEventListener('touchstart', this.onTouchStart);
 | 
	
		
			
				|  |  | +    canvasEl.addEventListener('touchend', this.onTouchEnd);
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * nav
 | 
	
		
			
				|  |  | - *
 | 
	
		
			
				|  |  | - * Pathfinding system, using PatrolJS.
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -module.exports = {
 | 
	
		
			
				|  |  | -  init: function () {
 | 
	
		
			
				|  |  | -    this.navMesh = null;
 | 
	
		
			
				|  |  | -    this.nodes = null;
 | 
	
		
			
				|  |  | -    this.controllers = new Set();
 | 
	
		
			
				|  |  | +  removeEventListeners: function () {
 | 
	
		
			
				|  |  | +    var canvasEl = this.el.sceneEl && this.el.sceneEl.canvas;
 | 
	
		
			
				|  |  | +    if (!canvasEl) { return; }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    canvasEl.removeEventListener('touchstart', this.onTouchStart);
 | 
	
		
			
				|  |  | +    canvasEl.removeEventListener('touchend', this.onTouchEnd);
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /**
 | 
	
		
			
				|  |  | -   * @param {THREE.Mesh} mesh
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | -  setNavMesh: function (mesh) {
 | 
	
		
			
				|  |  | -    var geometry = mesh.geometry.isBufferGeometry
 | 
	
		
			
				|  |  | -      ? new THREE.Geometry().fromBufferGeometry(mesh.geometry)
 | 
	
		
			
				|  |  | -      : mesh.geometry;
 | 
	
		
			
				|  |  | -    this.navMesh = new THREE.Mesh(geometry);
 | 
	
		
			
				|  |  | -    this.nodes = Path.buildNodes(this.navMesh.geometry);
 | 
	
		
			
				|  |  | -    Path.setZoneData('level', this.nodes);
 | 
	
		
			
				|  |  | +  isVelocityActive: function () {
 | 
	
		
			
				|  |  | +    return this.data.enabled && this.isMoving;
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /**
 | 
	
		
			
				|  |  | -   * @return {THREE.Mesh}
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | -  getNavMesh: function () {
 | 
	
		
			
				|  |  | -    return this.navMesh;
 | 
	
		
			
				|  |  | +  getVelocityDelta: function () {
 | 
	
		
			
				|  |  | +    this.dVelocity.z = this.isMoving ? -1 : 0;
 | 
	
		
			
				|  |  | +    return this.dVelocity.clone();
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /**
 | 
	
		
			
				|  |  | -   * @param {NavController} ctrl
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | -  addController: function (ctrl) {
 | 
	
		
			
				|  |  | -    this.controllers.add(ctrl);
 | 
	
		
			
				|  |  | +  bindMethods: function () {
 | 
	
		
			
				|  |  | +    this.onTouchStart = this.onTouchStart.bind(this);
 | 
	
		
			
				|  |  | +    this.onTouchEnd = this.onTouchEnd.bind(this);
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /**
 | 
	
		
			
				|  |  | -   * @param {NavController} ctrl
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | -  removeController: function (ctrl) {
 | 
	
		
			
				|  |  | -    this.controllers.remove(ctrl);
 | 
	
		
			
				|  |  | +  onTouchStart: function (e) {
 | 
	
		
			
				|  |  | +    this.isMoving = true;
 | 
	
		
			
				|  |  | +    e.preventDefault();
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /**
 | 
	
		
			
				|  |  | -   * @param  {NavController} ctrl
 | 
	
		
			
				|  |  | -   * @param  {THREE.Vector3} target
 | 
	
		
			
				|  |  | -   * @return {Array<THREE.Vector3>}
 | 
	
		
			
				|  |  | -   */
 | 
	
		
			
				|  |  | -  getPath: function (ctrl, target) {
 | 
	
		
			
				|  |  | -    var start = ctrl.el.object3D.position;
 | 
	
		
			
				|  |  | -    // TODO(donmccurdy): Current group should be cached.
 | 
	
		
			
				|  |  | -    var group = Path.getGroup('level', start);
 | 
	
		
			
				|  |  | -    return Path.findPath(start, target, 'level', group);
 | 
	
		
			
				|  |  | +  onTouchEnd: function (e) {
 | 
	
		
			
				|  |  | +    this.isMoving = false;
 | 
	
		
			
				|  |  | +    e.preventDefault();
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{"three-pathfinding":119}],111:[function(require,module,exports){
 | 
	
		
			
				|  |  | +},{}],93:[function(require,module,exports){
 | 
	
		
			
				|  |  |  /**
 | 
	
		
			
				|  |  | - * Flat grid.
 | 
	
		
			
				|  |  | + * Universal Controls
 | 
	
		
			
				|  |  |   *
 | 
	
		
			
				|  |  | - * Defaults to 75x75.
 | 
	
		
			
				|  |  | + * @author Don McCurdy <dm@donmccurdy.com>
 | 
	
		
			
				|  |  |   */
 | 
	
		
			
				|  |  | -var Primitive = module.exports = {
 | 
	
		
			
				|  |  | -  defaultComponents: {
 | 
	
		
			
				|  |  | -    geometry: {
 | 
	
		
			
				|  |  | -      primitive: 'plane',
 | 
	
		
			
				|  |  | -      width: 75,
 | 
	
		
			
				|  |  | -      height: 75
 | 
	
		
			
				|  |  | -    },
 | 
	
		
			
				|  |  | -    rotation: {x: -90, y: 0, z: 0},
 | 
	
		
			
				|  |  | -    material: {
 | 
	
		
			
				|  |  | -      src: 'url(https://cdn.rawgit.com/donmccurdy/aframe-extras/v1.16.3/assets/grid.png)',
 | 
	
		
			
				|  |  | -      repeat: '75 75'
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | -  mappings: {
 | 
	
		
			
				|  |  | -    width: 'geometry.width',
 | 
	
		
			
				|  |  | -    height: 'geometry.height',
 | 
	
		
			
				|  |  | -    src: 'material.src'
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -module.exports.registerAll = (function () {
 | 
	
		
			
				|  |  | -  var registered = false;
 | 
	
		
			
				|  |  | -  return function (AFRAME) {
 | 
	
		
			
				|  |  | -    if (registered) return;
 | 
	
		
			
				|  |  | -    AFRAME = AFRAME || window.AFRAME;
 | 
	
		
			
				|  |  | -    AFRAME.registerPrimitive('a-grid', Primitive);
 | 
	
		
			
				|  |  | -    registered = true;
 | 
	
		
			
				|  |  | -  };
 | 
	
		
			
				|  |  | -}());
 | 
	
		
			
				|  |  | +var COMPONENT_SUFFIX = '-controls',
 | 
	
		
			
				|  |  | +    MAX_DELTA = 0.2, // ms
 | 
	
		
			
				|  |  | +    PI_2 = Math.PI / 2;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{}],112:[function(require,module,exports){
 | 
	
		
			
				|  |  | -var vg = require('../../lib/hex-grid.min.js');
 | 
	
		
			
				|  |  | -var defaultHexGrid = require('../../lib/default-hex-grid.json');
 | 
	
		
			
				|  |  | +module.exports = {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * Hex grid.
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -var Primitive = module.exports.Primitive = {
 | 
	
		
			
				|  |  | -  defaultComponents: {
 | 
	
		
			
				|  |  | -    'hexgrid': {}
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | -  mappings: {
 | 
	
		
			
				|  |  | -    src: 'hexgrid.src'
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | +  /*******************************************************************
 | 
	
		
			
				|  |  | +   * Schema
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  dependencies: ['velocity', 'rotation'],
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -var Component = module.exports.Component = {
 | 
	
		
			
				|  |  | -  dependencies: ['material'],
 | 
	
		
			
				|  |  |    schema: {
 | 
	
		
			
				|  |  | -    src: {type: 'asset'}
 | 
	
		
			
				|  |  | +    enabled:              { default: true },
 | 
	
		
			
				|  |  | +    movementEnabled:      { default: true },
 | 
	
		
			
				|  |  | +    movementControls:     { default: ['gamepad', 'keyboard', 'touch', 'hmd'] },
 | 
	
		
			
				|  |  | +    rotationEnabled:      { default: true },
 | 
	
		
			
				|  |  | +    rotationControls:     { default: ['hmd', 'gamepad', 'mouse'] },
 | 
	
		
			
				|  |  | +    movementSpeed:        { default: 5 }, // m/s
 | 
	
		
			
				|  |  | +    movementEasing:       { default: 15 }, // m/s2
 | 
	
		
			
				|  |  | +    movementEasingY:      { default: 0  }, // m/s2
 | 
	
		
			
				|  |  | +    movementAcceleration: { default: 80 }, // m/s2
 | 
	
		
			
				|  |  | +    rotationSensitivity:  { default: 0.05 }, // radians/frame, ish
 | 
	
		
			
				|  |  | +    fly:                  { default: false },
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /*******************************************************************
 | 
	
		
			
				|  |  | +   * Lifecycle
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    init: function () {
 | 
	
		
			
				|  |  | -    var data = this.data;
 | 
	
		
			
				|  |  | -    if (data.src) {
 | 
	
		
			
				|  |  | -      fetch(data.src)
 | 
	
		
			
				|  |  | -        .then(function (response) { response.json(); })
 | 
	
		
			
				|  |  | -        .then(function (json) { this.addMesh(json); });
 | 
	
		
			
				|  |  | +    var rotation = this.el.getAttribute('rotation');
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (this.el.hasAttribute('look-controls') && this.data.rotationEnabled) {
 | 
	
		
			
				|  |  | +      console.error('[universal-controls] The `universal-controls` component is a replacement '
 | 
	
		
			
				|  |  | +        + 'for `look-controls`, and cannot be used in combination with it.');
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // Movement
 | 
	
		
			
				|  |  | +    this.velocity = new THREE.Vector3();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // Rotation
 | 
	
		
			
				|  |  | +    this.pitch = new THREE.Object3D();
 | 
	
		
			
				|  |  | +    this.pitch.rotation.x = THREE.Math.degToRad(rotation.x);
 | 
	
		
			
				|  |  | +    this.yaw = new THREE.Object3D();
 | 
	
		
			
				|  |  | +    this.yaw.position.y = 10;
 | 
	
		
			
				|  |  | +    this.yaw.rotation.y = THREE.Math.degToRad(rotation.y);
 | 
	
		
			
				|  |  | +    this.yaw.add(this.pitch);
 | 
	
		
			
				|  |  | +    this.heading = new THREE.Euler(0, 0, 0, 'YXZ');
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (this.el.sceneEl.hasLoaded) {
 | 
	
		
			
				|  |  | +      this.injectControls();
 | 
	
		
			
				|  |  |      } else {
 | 
	
		
			
				|  |  | -      this.addMesh(defaultHexGrid);
 | 
	
		
			
				|  |  | +      this.el.sceneEl.addEventListener('loaded', this.injectControls.bind(this));
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  | -  addMesh: function (json) {
 | 
	
		
			
				|  |  | -    var grid = new vg.HexGrid();
 | 
	
		
			
				|  |  | -    grid.fromJSON(json);
 | 
	
		
			
				|  |  | -    var board = new vg.Board(grid);
 | 
	
		
			
				|  |  | -    board.generateTilemap();
 | 
	
		
			
				|  |  | -    this.el.setObject3D('mesh', board.group);
 | 
	
		
			
				|  |  | -    this.addMaterial();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  update: function () {
 | 
	
		
			
				|  |  | +    if (this.el.sceneEl.hasLoaded) {
 | 
	
		
			
				|  |  | +      this.injectControls();
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  | -  addMaterial: function () {
 | 
	
		
			
				|  |  | -    var materialComponent = this.el.components.material;
 | 
	
		
			
				|  |  | -    var material = (materialComponent || {}).material;
 | 
	
		
			
				|  |  | -    if (!material) return;
 | 
	
		
			
				|  |  | -    this.el.object3D.traverse(function (node) {
 | 
	
		
			
				|  |  | -      if (node.isMesh) {
 | 
	
		
			
				|  |  | -        node.material = material;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  injectControls: function () {
 | 
	
		
			
				|  |  | +    var i, name,
 | 
	
		
			
				|  |  | +        data = this.data;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    for (i = 0; i < data.movementControls.length; i++) {
 | 
	
		
			
				|  |  | +      name = data.movementControls[i] + COMPONENT_SUFFIX;
 | 
	
		
			
				|  |  | +      if (!this.el.components[name]) {
 | 
	
		
			
				|  |  | +        this.el.setAttribute(name, '');
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | -    });
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    for (i = 0; i < data.rotationControls.length; i++) {
 | 
	
		
			
				|  |  | +      name = data.rotationControls[i] + COMPONENT_SUFFIX;
 | 
	
		
			
				|  |  | +      if (!this.el.components[name]) {
 | 
	
		
			
				|  |  | +        this.el.setAttribute(name, '');
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  | -  remove: function () {
 | 
	
		
			
				|  |  | -    this.el.removeObject3D('mesh');
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -module.exports.registerAll = (function () {
 | 
	
		
			
				|  |  | -  var registered = false;
 | 
	
		
			
				|  |  | -  return function (AFRAME) {
 | 
	
		
			
				|  |  | -    if (registered) return;
 | 
	
		
			
				|  |  | -    AFRAME = AFRAME || window.AFRAME;
 | 
	
		
			
				|  |  | -    AFRAME.registerComponent('hexgrid', Component);
 | 
	
		
			
				|  |  | -    AFRAME.registerPrimitive('a-hexgrid', Primitive);
 | 
	
		
			
				|  |  | -    registered = true;
 | 
	
		
			
				|  |  | -  };
 | 
	
		
			
				|  |  | -}());
 | 
	
		
			
				|  |  | +  /*******************************************************************
 | 
	
		
			
				|  |  | +   * Tick
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{"../../lib/default-hex-grid.json":7,"../../lib/hex-grid.min.js":9}],113:[function(require,module,exports){
 | 
	
		
			
				|  |  | -/**
 | 
	
		
			
				|  |  | - * Flat-shaded ocean primitive.
 | 
	
		
			
				|  |  | - *
 | 
	
		
			
				|  |  | - * Based on a Codrops tutorial:
 | 
	
		
			
				|  |  | - * http://tympanus.net/codrops/2016/04/26/the-aviator-animating-basic-3d-scene-threejs/
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -var Primitive = module.exports.Primitive = {
 | 
	
		
			
				|  |  | -  defaultComponents: {
 | 
	
		
			
				|  |  | -    ocean: {},
 | 
	
		
			
				|  |  | -    rotation: {x: -90, y: 0, z: 0}
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | -  mappings: {
 | 
	
		
			
				|  |  | -    width: 'ocean.width',
 | 
	
		
			
				|  |  | -    depth: 'ocean.depth',
 | 
	
		
			
				|  |  | -    density: 'ocean.density',
 | 
	
		
			
				|  |  | -    color: 'ocean.color',
 | 
	
		
			
				|  |  | -    opacity: 'ocean.opacity'
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | +  tick: function (t, dt) {
 | 
	
		
			
				|  |  | +    if (!dt) { return; }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -var Component = module.exports.Component = {
 | 
	
		
			
				|  |  | -  schema: {
 | 
	
		
			
				|  |  | -    // Dimensions of the ocean area.
 | 
	
		
			
				|  |  | -    width: {default: 10, min: 0},
 | 
	
		
			
				|  |  | -    depth: {default: 10, min: 0},
 | 
	
		
			
				|  |  | +    // Update rotation.
 | 
	
		
			
				|  |  | +    if (this.data.rotationEnabled) this.updateRotation(dt);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // Density of waves.
 | 
	
		
			
				|  |  | -    density: {default: 10},
 | 
	
		
			
				|  |  | +    // Update velocity. If FPS is too low, reset.
 | 
	
		
			
				|  |  | +    if (this.data.movementEnabled && dt / 1000 > MAX_DELTA) {
 | 
	
		
			
				|  |  | +      this.velocity.set(0, 0, 0);
 | 
	
		
			
				|  |  | +      this.el.setAttribute('velocity', this.velocity);
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +      this.updateVelocity(dt);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // Wave amplitude and variance.
 | 
	
		
			
				|  |  | -    amplitude: {default: 0.1},
 | 
	
		
			
				|  |  | -    amplitudeVariance: {default: 0.3},
 | 
	
		
			
				|  |  | +  /*******************************************************************
 | 
	
		
			
				|  |  | +   * Rotation
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // Wave speed and variance.
 | 
	
		
			
				|  |  | -    speed: {default: 1},
 | 
	
		
			
				|  |  | -    speedVariance: {default: 2},
 | 
	
		
			
				|  |  | +  updateRotation: function (dt) {
 | 
	
		
			
				|  |  | +    var control, dRotation,
 | 
	
		
			
				|  |  | +        data = this.data;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // Material.
 | 
	
		
			
				|  |  | -    color: {default: '#7AD2F7', type: 'color'},
 | 
	
		
			
				|  |  | -    opacity: {default: 0.8}
 | 
	
		
			
				|  |  | +    for (var i = 0, l = data.rotationControls.length; i < l; i++) {
 | 
	
		
			
				|  |  | +      control = this.el.components[data.rotationControls[i] + COMPONENT_SUFFIX];
 | 
	
		
			
				|  |  | +      if (control && control.isRotationActive()) {
 | 
	
		
			
				|  |  | +        if (control.getRotationDelta) {
 | 
	
		
			
				|  |  | +          dRotation = control.getRotationDelta(dt);
 | 
	
		
			
				|  |  | +          dRotation.multiplyScalar(data.rotationSensitivity);
 | 
	
		
			
				|  |  | +          this.yaw.rotation.y -= dRotation.x;
 | 
	
		
			
				|  |  | +          this.pitch.rotation.x -= dRotation.y;
 | 
	
		
			
				|  |  | +          this.pitch.rotation.x = Math.max(-PI_2, Math.min(PI_2, this.pitch.rotation.x));
 | 
	
		
			
				|  |  | +          this.el.setAttribute('rotation', {
 | 
	
		
			
				|  |  | +            x: THREE.Math.radToDeg(this.pitch.rotation.x),
 | 
	
		
			
				|  |  | +            y: THREE.Math.radToDeg(this.yaw.rotation.y),
 | 
	
		
			
				|  |  | +            z: 0
 | 
	
		
			
				|  |  | +          });
 | 
	
		
			
				|  |  | +        } else if (control.getRotation) {
 | 
	
		
			
				|  |  | +          this.el.setAttribute('rotation', control.getRotation());
 | 
	
		
			
				|  |  | +        } else {
 | 
	
		
			
				|  |  | +          throw new Error('Incompatible rotation controls: %s', data.rotationControls[i]);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        break;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  /**
 | 
	
		
			
				|  |  | -   * Use play() instead of init(), because component mappings – unavailable as dependencies – are
 | 
	
		
			
				|  |  | -   * not guaranteed to have parsed when this component is initialized.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /*******************************************************************
 | 
	
		
			
				|  |  | +   * Movement
 | 
	
		
			
				|  |  |     */
 | 
	
		
			
				|  |  | -  play: function () {
 | 
	
		
			
				|  |  | -    var el = this.el,
 | 
	
		
			
				|  |  | -        data = this.data,
 | 
	
		
			
				|  |  | -        material = el.components.material;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    var geometry = new THREE.PlaneGeometry(data.width, data.depth, data.density, data.density);
 | 
	
		
			
				|  |  | -    geometry.mergeVertices();
 | 
	
		
			
				|  |  | -    this.waves = [];
 | 
	
		
			
				|  |  | -    for (var v, i = 0, l = geometry.vertices.length; i < l; i++) {
 | 
	
		
			
				|  |  | -      v = geometry.vertices[i];
 | 
	
		
			
				|  |  | -      this.waves.push({
 | 
	
		
			
				|  |  | -        z: v.z,
 | 
	
		
			
				|  |  | -        ang: Math.random() * Math.PI * 2,
 | 
	
		
			
				|  |  | -        amp: data.amplitude + Math.random() * data.amplitudeVariance,
 | 
	
		
			
				|  |  | -        speed: (data.speed + Math.random() * data.speedVariance) / 1000 // radians / frame
 | 
	
		
			
				|  |  | -      });
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +  updateVelocity: function (dt) {
 | 
	
		
			
				|  |  | +    var control, dVelocity,
 | 
	
		
			
				|  |  | +        velocity = this.velocity,
 | 
	
		
			
				|  |  | +        data = this.data;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (!material) {
 | 
	
		
			
				|  |  | -      material = {};
 | 
	
		
			
				|  |  | -      material.material = new THREE.MeshPhongMaterial({
 | 
	
		
			
				|  |  | -        color: data.color,
 | 
	
		
			
				|  |  | -        transparent: data.opacity < 1,
 | 
	
		
			
				|  |  | -        opacity: data.opacity,
 | 
	
		
			
				|  |  | -        shading: THREE.FlatShading,
 | 
	
		
			
				|  |  | -      });
 | 
	
		
			
				|  |  | +    if (data.movementEnabled) {
 | 
	
		
			
				|  |  | +      for (var i = 0, l = data.movementControls.length; i < l; i++) {
 | 
	
		
			
				|  |  | +        control = this.el.components[data.movementControls[i] + COMPONENT_SUFFIX];
 | 
	
		
			
				|  |  | +        if (control && control.isVelocityActive()) {
 | 
	
		
			
				|  |  | +          if (control.getVelocityDelta) {
 | 
	
		
			
				|  |  | +            dVelocity = control.getVelocityDelta(dt);
 | 
	
		
			
				|  |  | +          } else if (control.getVelocity) {
 | 
	
		
			
				|  |  | +            this.el.setAttribute('velocity', control.getVelocity());
 | 
	
		
			
				|  |  | +            return;
 | 
	
		
			
				|  |  | +          } else if (control.getPositionDelta) {
 | 
	
		
			
				|  |  | +            velocity.copy(control.getPositionDelta(dt).multiplyScalar(1000 / dt));
 | 
	
		
			
				|  |  | +            this.el.setAttribute('velocity', velocity);
 | 
	
		
			
				|  |  | +            return;
 | 
	
		
			
				|  |  | +          } else {
 | 
	
		
			
				|  |  | +            throw new Error('Incompatible movement controls: ', data.movementControls[i]);
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  | +          break;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    this.mesh = new THREE.Mesh(geometry, material.material);
 | 
	
		
			
				|  |  | -    el.setObject3D('mesh', this.mesh);
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +    velocity.copy(this.el.getAttribute('velocity'));
 | 
	
		
			
				|  |  | +    velocity.x -= velocity.x * data.movementEasing * dt / 1000;
 | 
	
		
			
				|  |  | +    velocity.y -= velocity.y * data.movementEasingY * dt / 1000;
 | 
	
		
			
				|  |  | +    velocity.z -= velocity.z * data.movementEasing * dt / 1000;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  remove: function () {
 | 
	
		
			
				|  |  | -    this.el.removeObject3D('mesh');
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | +    if (dVelocity && data.movementEnabled) {
 | 
	
		
			
				|  |  | +      // Set acceleration
 | 
	
		
			
				|  |  | +      if (dVelocity.length() > 1) {
 | 
	
		
			
				|  |  | +        dVelocity.setLength(this.data.movementAcceleration * dt / 1000);
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        dVelocity.multiplyScalar(this.data.movementAcceleration * dt / 1000);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  tick: function (t, dt) {
 | 
	
		
			
				|  |  | -    if (!dt) return;
 | 
	
		
			
				|  |  | +      // Rotate to heading
 | 
	
		
			
				|  |  | +      var rotation = this.el.getAttribute('rotation');
 | 
	
		
			
				|  |  | +      if (rotation) {
 | 
	
		
			
				|  |  | +        this.heading.set(
 | 
	
		
			
				|  |  | +          data.fly ? THREE.Math.degToRad(rotation.x) : 0,
 | 
	
		
			
				|  |  | +          THREE.Math.degToRad(rotation.y),
 | 
	
		
			
				|  |  | +          0
 | 
	
		
			
				|  |  | +        );
 | 
	
		
			
				|  |  | +        dVelocity.applyEuler(this.heading);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    var verts = this.mesh.geometry.vertices;
 | 
	
		
			
				|  |  | -    for (var v, vprops, i = 0; (v = verts[i]); i++){
 | 
	
		
			
				|  |  | -      vprops = this.waves[i];
 | 
	
		
			
				|  |  | -      v.z = vprops.z + Math.sin(vprops.ang) * vprops.amp;
 | 
	
		
			
				|  |  | -      vprops.ang += vprops.speed * dt;
 | 
	
		
			
				|  |  | +      velocity.add(dVelocity);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      // TODO - Several issues here:
 | 
	
		
			
				|  |  | +      // (1) Interferes w/ gravity.
 | 
	
		
			
				|  |  | +      // (2) Interferes w/ jumping.
 | 
	
		
			
				|  |  | +      // (3) Likely to interfere w/ relative position to moving platform.
 | 
	
		
			
				|  |  | +      // if (velocity.length() > data.movementSpeed) {
 | 
	
		
			
				|  |  | +      //   velocity.setLength(data.movementSpeed);
 | 
	
		
			
				|  |  | +      // }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -    this.mesh.geometry.verticesNeedUpdate = true;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    this.el.setAttribute('velocity', velocity);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -module.exports.registerAll = (function () {
 | 
	
		
			
				|  |  | -  var registered = false;
 | 
	
		
			
				|  |  | -  return function (AFRAME) {
 | 
	
		
			
				|  |  | -    if (registered) return;
 | 
	
		
			
				|  |  | -    AFRAME = AFRAME || window.AFRAME;
 | 
	
		
			
				|  |  | -    AFRAME.registerComponent('ocean', Component);
 | 
	
		
			
				|  |  | -    AFRAME.registerPrimitive('a-ocean', Primitive);
 | 
	
		
			
				|  |  | -    registered = true;
 | 
	
		
			
				|  |  | -  };
 | 
	
		
			
				|  |  | -}());
 | 
	
		
			
				|  |  | +},{}],94:[function(require,module,exports){
 | 
	
		
			
				|  |  | +var LoopMode = {
 | 
	
		
			
				|  |  | +  once: THREE.LoopOnce,
 | 
	
		
			
				|  |  | +  repeat: THREE.LoopRepeat,
 | 
	
		
			
				|  |  | +  pingpong: THREE.LoopPingPong
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{}],114:[function(require,module,exports){
 | 
	
		
			
				|  |  |  /**
 | 
	
		
			
				|  |  | - * Tube following a custom path.
 | 
	
		
			
				|  |  | - *
 | 
	
		
			
				|  |  | - * Usage:
 | 
	
		
			
				|  |  | + * animation-mixer
 | 
	
		
			
				|  |  |   *
 | 
	
		
			
				|  |  | - * ```html
 | 
	
		
			
				|  |  | - * <a-tube path="5 0 5, 5 0 -5, -5 0 -5" radius="0.5"></a-tube>
 | 
	
		
			
				|  |  | - * ```
 | 
	
		
			
				|  |  | + * Player for animation clips. Intended to be compatible with any model format that supports
 | 
	
		
			
				|  |  | + * skeletal or morph animations through THREE.AnimationMixer.
 | 
	
		
			
				|  |  | + * See: https://threejs.org/docs/?q=animation#Reference/Animation/AnimationMixer
 | 
	
		
			
				|  |  |   */
 | 
	
		
			
				|  |  | -var Primitive = module.exports.Primitive = {
 | 
	
		
			
				|  |  | -  defaultComponents: {
 | 
	
		
			
				|  |  | -    tube:           {},
 | 
	
		
			
				|  |  | -  },
 | 
	
		
			
				|  |  | -  mappings: {
 | 
	
		
			
				|  |  | -    path:           'tube.path',
 | 
	
		
			
				|  |  | -    segments:       'tube.segments',
 | 
	
		
			
				|  |  | -    radius:         'tube.radius',
 | 
	
		
			
				|  |  | -    radialSegments: 'tube.radialSegments',
 | 
	
		
			
				|  |  | -    closed:         'tube.closed'
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -var Component = module.exports.Component = {
 | 
	
		
			
				|  |  | +module.exports = {
 | 
	
		
			
				|  |  |    schema: {
 | 
	
		
			
				|  |  | -    path:           {default: []},
 | 
	
		
			
				|  |  | -    segments:       {default: 64},
 | 
	
		
			
				|  |  | -    radius:         {default: 1},
 | 
	
		
			
				|  |  | -    radialSegments: {default: 8},
 | 
	
		
			
				|  |  | -    closed:         {default: false}
 | 
	
		
			
				|  |  | +    clip:  {default: '*'},
 | 
	
		
			
				|  |  | +    duration: {default: 0},
 | 
	
		
			
				|  |  | +    crossFadeDuration: {default: 0},
 | 
	
		
			
				|  |  | +    loop: {default: 'repeat', oneOf: Object.keys(LoopMode)},
 | 
	
		
			
				|  |  | +    repetitions: {default: Infinity, min: 0}
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    init: function () {
 | 
	
		
			
				|  |  | -    var el = this.el,
 | 
	
		
			
				|  |  | -        data = this.data,
 | 
	
		
			
				|  |  | -        material = el.components.material;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    if (!data.path.length) {
 | 
	
		
			
				|  |  | -      console.error('[a-tube] `path` property expected but not found.');
 | 
	
		
			
				|  |  | -      return;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +    /** @type {THREE.Mesh} */
 | 
	
		
			
				|  |  | +    this.model = null;
 | 
	
		
			
				|  |  | +    /** @type {THREE.AnimationMixer} */
 | 
	
		
			
				|  |  | +    this.mixer = null;
 | 
	
		
			
				|  |  | +    /** @type {Array<THREE.AnimationAction>} */
 | 
	
		
			
				|  |  | +    this.activeActions = [];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    var curve = new THREE.CatmullRomCurve3(data.path.map(function (point) {
 | 
	
		
			
				|  |  | -      point = point.split(' ');
 | 
	
		
			
				|  |  | -      return new THREE.Vector3(Number(point[0]), Number(point[1]), Number(point[2]));
 | 
	
		
			
				|  |  | -    }));
 | 
	
		
			
				|  |  | -    var geometry = new THREE.TubeGeometry(
 | 
	
		
			
				|  |  | -      curve, data.segments, data.radius, data.radialSegments, data.closed
 | 
	
		
			
				|  |  | -    );
 | 
	
		
			
				|  |  | +    var model = this.el.getObject3D('mesh');
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (!material) {
 | 
	
		
			
				|  |  | -      material = {};
 | 
	
		
			
				|  |  | -      material.material = new THREE.MeshPhongMaterial();
 | 
	
		
			
				|  |  | +    if (model) {
 | 
	
		
			
				|  |  | +      this.load(model);
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +      this.el.addEventListener('model-loaded', function(e) {
 | 
	
		
			
				|  |  | +        this.load(e.detail.model);
 | 
	
		
			
				|  |  | +      }.bind(this));
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    this.mesh = new THREE.Mesh(geometry, material.material);
 | 
	
		
			
				|  |  | -    this.el.setObject3D('mesh', this.mesh);
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  remove: function () {
 | 
	
		
			
				|  |  | -    if (this.mesh) this.el.removeObject3D('mesh');
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -module.exports.registerAll = (function () {
 | 
	
		
			
				|  |  | -  var registered = false;
 | 
	
		
			
				|  |  | -  return function (AFRAME) {
 | 
	
		
			
				|  |  | -    if (registered) return;
 | 
	
		
			
				|  |  | -    AFRAME = AFRAME || window.AFRAME;
 | 
	
		
			
				|  |  | -    AFRAME.registerComponent('tube', Component);
 | 
	
		
			
				|  |  | -    AFRAME.registerPrimitive('a-tube', Primitive);
 | 
	
		
			
				|  |  | -    registered = true;
 | 
	
		
			
				|  |  | -  };
 | 
	
		
			
				|  |  | -}());
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -},{}],115:[function(require,module,exports){
 | 
	
		
			
				|  |  | -module.exports = {
 | 
	
		
			
				|  |  | -  'a-grid':     require('./a-grid'),
 | 
	
		
			
				|  |  | -  'a-hexgrid': require('./a-hexgrid'),
 | 
	
		
			
				|  |  | -  'a-ocean':    require('./a-ocean'),
 | 
	
		
			
				|  |  | -  'a-tube':     require('./a-tube'),
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  registerAll: function (AFRAME) {
 | 
	
		
			
				|  |  | -    if (this._registered) return;
 | 
	
		
			
				|  |  | -    AFRAME = AFRAME || window.AFRAME;
 | 
	
		
			
				|  |  | -    this['a-grid'].registerAll(AFRAME);
 | 
	
		
			
				|  |  | -    this['a-hexgrid'].registerAll(AFRAME);
 | 
	
		
			
				|  |  | -    this['a-ocean'].registerAll(AFRAME);
 | 
	
		
			
				|  |  | -    this['a-tube'].registerAll(AFRAME);
 | 
	
		
			
				|  |  | -    this._registered = true;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -},{"./a-grid":111,"./a-hexgrid":112,"./a-ocean":113,"./a-tube":114}],116:[function(require,module,exports){
 | 
	
		
			
				|  |  | -const BinaryHeap = require('./BinaryHeap');
 | 
	
		
			
				|  |  | -const utils = require('./utils.js');
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -class AStar {
 | 
	
		
			
				|  |  | -  static init (graph) {
 | 
	
		
			
				|  |  | -    for (let x = 0; x < graph.length; x++) {
 | 
	
		
			
				|  |  | -      //for(var x in graph) {
 | 
	
		
			
				|  |  | -      const node = graph[x];
 | 
	
		
			
				|  |  | -      node.f = 0;
 | 
	
		
			
				|  |  | -      node.g = 0;
 | 
	
		
			
				|  |  | -      node.h = 0;
 | 
	
		
			
				|  |  | -      node.cost = 1.0;
 | 
	
		
			
				|  |  | -      node.visited = false;
 | 
	
		
			
				|  |  | -      node.closed = false;
 | 
	
		
			
				|  |  | -      node.parent = null;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  static cleanUp (graph) {
 | 
	
		
			
				|  |  | -    for (let x = 0; x < graph.length; x++) {
 | 
	
		
			
				|  |  | -      const node = graph[x];
 | 
	
		
			
				|  |  | -      delete node.f;
 | 
	
		
			
				|  |  | -      delete node.g;
 | 
	
		
			
				|  |  | -      delete node.h;
 | 
	
		
			
				|  |  | -      delete node.cost;
 | 
	
		
			
				|  |  | -      delete node.visited;
 | 
	
		
			
				|  |  | -      delete node.closed;
 | 
	
		
			
				|  |  | -      delete node.parent;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +  load: function (model) {
 | 
	
		
			
				|  |  | +    var el = this.el;
 | 
	
		
			
				|  |  | +    this.model = model;
 | 
	
		
			
				|  |  | +    this.mixer = new THREE.AnimationMixer(model);
 | 
	
		
			
				|  |  | +    this.mixer.addEventListener('loop', function (e) {
 | 
	
		
			
				|  |  | +      el.emit('animation-loop', {action: e.action, loopDelta: e.loopDelta});
 | 
	
		
			
				|  |  | +    }.bind(this));
 | 
	
		
			
				|  |  | +    this.mixer.addEventListener('finished', function (e) {
 | 
	
		
			
				|  |  | +      el.emit('animation-finished', {action: e.action, direction: e.direction});
 | 
	
		
			
				|  |  | +    }.bind(this));
 | 
	
		
			
				|  |  | +    if (this.data.clip) this.update({});
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  static heap () {
 | 
	
		
			
				|  |  | -    return new BinaryHeap(function (node) {
 | 
	
		
			
				|  |  | -      return node.f;
 | 
	
		
			
				|  |  | -    });
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +  remove: function () {
 | 
	
		
			
				|  |  | +    if (this.mixer) this.mixer.stopAllAction();
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  static search (graph, start, end) {
 | 
	
		
			
				|  |  | -    this.init(graph);
 | 
	
		
			
				|  |  | -    //heuristic = heuristic || astar.manhattan;
 | 
	
		
			
				|  |  | +  update: function (previousData) {
 | 
	
		
			
				|  |  | +    if (!previousData) return;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    this.stopAction();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    const openHeap = this.heap();
 | 
	
		
			
				|  |  | +    if (this.data.clip) {
 | 
	
		
			
				|  |  | +      this.playAction();
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    openHeap.push(start);
 | 
	
		
			
				|  |  | +  stopAction: function () {
 | 
	
		
			
				|  |  | +    var data = this.data;
 | 
	
		
			
				|  |  | +    for (var i = 0; i < this.activeActions.length; i++) {
 | 
	
		
			
				|  |  | +      data.crossFadeDuration
 | 
	
		
			
				|  |  | +        ? this.activeActions[i].fadeOut(data.crossFadeDuration)
 | 
	
		
			
				|  |  | +        : this.activeActions[i].stop();
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    this.activeActions.length = 0;
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    while (openHeap.size() > 0) {
 | 
	
		
			
				|  |  | +  playAction: function () {
 | 
	
		
			
				|  |  | +    if (!this.mixer) return;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      // Grab the lowest f(x) to process next.  Heap keeps this sorted for us.
 | 
	
		
			
				|  |  | -      const currentNode = openHeap.pop();
 | 
	
		
			
				|  |  | +    var model = this.model,
 | 
	
		
			
				|  |  | +        data = this.data,
 | 
	
		
			
				|  |  | +        clips = model.animations || (model.geometry || {}).animations || [];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      // End case -- result has been found, return the traced path.
 | 
	
		
			
				|  |  | -      if (currentNode === end) {
 | 
	
		
			
				|  |  | -        let curr = currentNode;
 | 
	
		
			
				|  |  | -        const ret = [];
 | 
	
		
			
				|  |  | -        while (curr.parent) {
 | 
	
		
			
				|  |  | -          ret.push(curr);
 | 
	
		
			
				|  |  | -          curr = curr.parent;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        this.cleanUp(ret);
 | 
	
		
			
				|  |  | -        return ret.reverse();
 | 
	
		
			
				|  |  | +    if (!clips.length) return;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    var re = wildcardToRegExp(data.clip);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    for (var clip, i = 0; (clip = clips[i]); i++) {
 | 
	
		
			
				|  |  | +      if (clip.name.match(re)) {
 | 
	
		
			
				|  |  | +        var action = this.mixer.clipAction(clip, model);
 | 
	
		
			
				|  |  | +        action.enabled = true;
 | 
	
		
			
				|  |  | +        if (data.duration) action.setDuration(data.duration);
 | 
	
		
			
				|  |  | +        action
 | 
	
		
			
				|  |  | +          .setLoop(LoopMode[data.loop], data.repetitions)
 | 
	
		
			
				|  |  | +          .fadeIn(data.crossFadeDuration)
 | 
	
		
			
				|  |  | +          .play();
 | 
	
		
			
				|  |  | +        this.activeActions.push(action);
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      // Normal case -- move currentNode from open to closed, process each of its neighbours.
 | 
	
		
			
				|  |  | -      currentNode.closed = true;
 | 
	
		
			
				|  |  | +  tick: function (t, dt) {
 | 
	
		
			
				|  |  | +    if (this.mixer && !isNaN(dt)) this.mixer.update(dt / 1000);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      // Find all neighbours for the current node. Optionally find diagonal neighbours as well (false by default).
 | 
	
		
			
				|  |  | -      const neighbours = this.neighbours(graph, currentNode);
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Creates a RegExp from the given string, converting asterisks to .* expressions,
 | 
	
		
			
				|  |  | + * and escaping all other characters.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +function wildcardToRegExp (s) {
 | 
	
		
			
				|  |  | +  return new RegExp('^' + s.split(/\*+/).map(regExpEscape).join('.*') + '$');
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      for (let i = 0, il = neighbours.length; i < il; i++) {
 | 
	
		
			
				|  |  | -        const neighbour = neighbours[i];
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * RegExp-escapes all characters in the given string.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +function regExpEscape (s) {
 | 
	
		
			
				|  |  | +  return s.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&');
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        if (neighbour.closed) {
 | 
	
		
			
				|  |  | -          // Not a valid node to process, skip to next neighbour.
 | 
	
		
			
				|  |  | -          continue;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | +},{}],95:[function(require,module,exports){
 | 
	
		
			
				|  |  | +THREE.FBXLoader = require('../../lib/FBXLoader');
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        // The g score is the shortest distance from start to current node.
 | 
	
		
			
				|  |  | -        // We need to check if the path we have arrived at this neighbour is the shortest one we have seen yet.
 | 
	
		
			
				|  |  | -        const gScore = currentNode.g + neighbour.cost;
 | 
	
		
			
				|  |  | -        const beenVisited = neighbour.visited;
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * fbx-model
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Loader for FBX format. Supports ASCII, but *not* binary, models.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +module.exports = {
 | 
	
		
			
				|  |  | +  schema: {
 | 
	
		
			
				|  |  | +    src:         { type: 'asset' },
 | 
	
		
			
				|  |  | +    crossorigin: { default: '' }
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        if (!beenVisited || gScore < neighbour.g) {
 | 
	
		
			
				|  |  | +  init: function () {
 | 
	
		
			
				|  |  | +    this.model = null;
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -          // Found an optimal (so far) path to this node.  Take score for node to see how good it is.
 | 
	
		
			
				|  |  | -          neighbour.visited = true;
 | 
	
		
			
				|  |  | -          neighbour.parent = currentNode;
 | 
	
		
			
				|  |  | -          if (!neighbour.centroid || !end.centroid) throw new Error('Unexpected state');
 | 
	
		
			
				|  |  | -          neighbour.h = neighbour.h || this.heuristic(neighbour.centroid, end.centroid);
 | 
	
		
			
				|  |  | -          neighbour.g = gScore;
 | 
	
		
			
				|  |  | -          neighbour.f = neighbour.g + neighbour.h;
 | 
	
		
			
				|  |  | +  update: function () {
 | 
	
		
			
				|  |  | +    var loader,
 | 
	
		
			
				|  |  | +        data = this.data;
 | 
	
		
			
				|  |  | +    if (!data.src) return;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -          if (!beenVisited) {
 | 
	
		
			
				|  |  | -            // Pushing to heap will put it in proper place based on the 'f' value.
 | 
	
		
			
				|  |  | -            openHeap.push(neighbour);
 | 
	
		
			
				|  |  | -          } else {
 | 
	
		
			
				|  |  | -            // Already seen the node, but since it has been rescored we need to reorder it in the heap
 | 
	
		
			
				|  |  | -            openHeap.rescoreElement(neighbour);
 | 
	
		
			
				|  |  | -          }
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +    this.remove();
 | 
	
		
			
				|  |  | +    loader = new THREE.FBXLoader();
 | 
	
		
			
				|  |  | +    if (data.crossorigin) loader.setCrossOrigin(data.crossorigin);
 | 
	
		
			
				|  |  | +    loader.load(data.src, this.load.bind(this));
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // No result was found - empty array signifies failure to find path.
 | 
	
		
			
				|  |  | -    return [];
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +  load: function (model) {
 | 
	
		
			
				|  |  | +    this.model = model;
 | 
	
		
			
				|  |  | +    this.el.setObject3D('mesh', model);
 | 
	
		
			
				|  |  | +    this.el.emit('model-loaded', {format: 'fbx', model: model});
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  static heuristic (pos1, pos2) {
 | 
	
		
			
				|  |  | -    return utils.distanceToSquared(pos1, pos2);
 | 
	
		
			
				|  |  | +  remove: function () {
 | 
	
		
			
				|  |  | +    if (this.model) this.el.removeObject3D('mesh');
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  static neighbours (graph, node) {
 | 
	
		
			
				|  |  | -    const ret = [];
 | 
	
		
			
				|  |  | +},{"../../lib/FBXLoader":3}],96:[function(require,module,exports){
 | 
	
		
			
				|  |  | +var fetchScript = require('../../lib/fetch-script')();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    for (let e = 0; e < node.neighbours.length; e++) {
 | 
	
		
			
				|  |  | -      ret.push(graph[node.neighbours[e]]);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +var LOADER_SRC = 'https://rawgit.com/mrdoob/three.js/r86/examples/js/loaders/GLTFLoader.js';
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    return ret;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Legacy loader for glTF 1.0 models.
 | 
	
		
			
				|  |  | + * Asynchronously loads THREE.GLTFLoader from rawgit.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +module.exports = {
 | 
	
		
			
				|  |  | +  schema: {type: 'model'},
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -module.exports = AStar;
 | 
	
		
			
				|  |  | +  init: function () {
 | 
	
		
			
				|  |  | +    this.model = null;
 | 
	
		
			
				|  |  | +    this.loader = null;
 | 
	
		
			
				|  |  | +    this.loaderPromise = loadLoader().then(function () {
 | 
	
		
			
				|  |  | +      this.loader = new THREE.GLTFLoader();
 | 
	
		
			
				|  |  | +      this.loader.setCrossOrigin('Anonymous');
 | 
	
		
			
				|  |  | +    }.bind(this));
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{"./BinaryHeap":117,"./utils.js":120}],117:[function(require,module,exports){
 | 
	
		
			
				|  |  | -// javascript-astar
 | 
	
		
			
				|  |  | -// http://github.com/bgrins/javascript-astar
 | 
	
		
			
				|  |  | -// Freely distributable under the MIT License.
 | 
	
		
			
				|  |  | -// Implements the astar search algorithm in javascript using a binary heap.
 | 
	
		
			
				|  |  | +  update: function () {
 | 
	
		
			
				|  |  | +    var self = this;
 | 
	
		
			
				|  |  | +    var el = this.el;
 | 
	
		
			
				|  |  | +    var src = this.data;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -class BinaryHeap {
 | 
	
		
			
				|  |  | -  constructor (scoreFunction) {
 | 
	
		
			
				|  |  | -    this.content = [];
 | 
	
		
			
				|  |  | -    this.scoreFunction = scoreFunction;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +    if (!src) { return; }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  push (element) {
 | 
	
		
			
				|  |  | -    // Add the new element to the end of the array.
 | 
	
		
			
				|  |  | -    this.content.push(element);
 | 
	
		
			
				|  |  | +    this.remove();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // Allow it to sink down.
 | 
	
		
			
				|  |  | -    this.sinkDown(this.content.length - 1);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +    this.loaderPromise.then(function () {
 | 
	
		
			
				|  |  | +      this.loader.load(src, function gltfLoaded (gltfModel) {
 | 
	
		
			
				|  |  | +        self.model = gltfModel.scene;
 | 
	
		
			
				|  |  | +        self.model.animations = gltfModel.animations;
 | 
	
		
			
				|  |  | +        el.setObject3D('mesh', self.model);
 | 
	
		
			
				|  |  | +        el.emit('model-loaded', {format: 'gltf', model: self.model});
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  | +    }.bind(this));
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  pop () {
 | 
	
		
			
				|  |  | -    // Store the first element so we can return it later.
 | 
	
		
			
				|  |  | -    const result = this.content[0];
 | 
	
		
			
				|  |  | -    // Get the element at the end of the array.
 | 
	
		
			
				|  |  | -    const end = this.content.pop();
 | 
	
		
			
				|  |  | -    // If there are any elements left, put the end element at the
 | 
	
		
			
				|  |  | -    // start, and let it bubble up.
 | 
	
		
			
				|  |  | -    if (this.content.length > 0) {
 | 
	
		
			
				|  |  | -      this.content[0] = end;
 | 
	
		
			
				|  |  | -      this.bubbleUp(0);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    return result;
 | 
	
		
			
				|  |  | +  remove: function () {
 | 
	
		
			
				|  |  | +    if (!this.model) { return; }
 | 
	
		
			
				|  |  | +    this.el.removeObject3D('mesh');
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  remove (node) {
 | 
	
		
			
				|  |  | -    const i = this.content.indexOf(node);
 | 
	
		
			
				|  |  | +var loadLoader = (function () {
 | 
	
		
			
				|  |  | +  var promise;
 | 
	
		
			
				|  |  | +  return function () {
 | 
	
		
			
				|  |  | +    promise = promise || fetchScript(LOADER_SRC);
 | 
	
		
			
				|  |  | +    return promise;
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +}());
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // When it is found, the process seen in 'pop' is repeated
 | 
	
		
			
				|  |  | -    // to fill up the hole.
 | 
	
		
			
				|  |  | -    const end = this.content.pop();
 | 
	
		
			
				|  |  | +},{"../../lib/fetch-script":8}],97:[function(require,module,exports){
 | 
	
		
			
				|  |  | +module.exports = {
 | 
	
		
			
				|  |  | +  'animation-mixer': require('./animation-mixer'),
 | 
	
		
			
				|  |  | +  'fbx-model': require('./fbx-model'),
 | 
	
		
			
				|  |  | +  'gltf-model-legacy': require('./gltf-model-legacy'),
 | 
	
		
			
				|  |  | +  'json-model': require('./json-model'),
 | 
	
		
			
				|  |  | +  'object-model': require('./object-model'),
 | 
	
		
			
				|  |  | +  'ply-model': require('./ply-model'),
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  registerAll: function (AFRAME) {
 | 
	
		
			
				|  |  | +    if (this._registered) return;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    AFRAME = AFRAME || window.AFRAME;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // THREE.AnimationMixer
 | 
	
		
			
				|  |  | +    if (!AFRAME.components['animation-mixer']) {
 | 
	
		
			
				|  |  | +      AFRAME.registerComponent('animation-mixer', this['animation-mixer']);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // THREE.PlyLoader
 | 
	
		
			
				|  |  | +    if (!AFRAME.systems['ply-model']) {
 | 
	
		
			
				|  |  | +      AFRAME.registerSystem('ply-model', this['ply-model'].System);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    if (!AFRAME.components['ply-model']) {
 | 
	
		
			
				|  |  | +      AFRAME.registerComponent('ply-model', this['ply-model'].Component);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (i !== this.content.length - 1) {
 | 
	
		
			
				|  |  | -      this.content[i] = end;
 | 
	
		
			
				|  |  | +    // THREE.FBXLoader
 | 
	
		
			
				|  |  | +    if (!AFRAME.components['fbx-model']) {
 | 
	
		
			
				|  |  | +      AFRAME.registerComponent('fbx-model', this['fbx-model']);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      if (this.scoreFunction(end) < this.scoreFunction(node)) {
 | 
	
		
			
				|  |  | -        this.sinkDown(i);
 | 
	
		
			
				|  |  | -      } else {
 | 
	
		
			
				|  |  | -        this.bubbleUp(i);
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | +    // THREE.GLTFLoader
 | 
	
		
			
				|  |  | +    if (!AFRAME.components['gltf-model-legacy']) {
 | 
	
		
			
				|  |  | +      AFRAME.registerComponent('gltf-model-legacy', this['gltf-model-legacy']);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  size () {
 | 
	
		
			
				|  |  | -    return this.content.length;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +    // THREE.JsonLoader
 | 
	
		
			
				|  |  | +    if (!AFRAME.components['json-model']) {
 | 
	
		
			
				|  |  | +      AFRAME.registerComponent('json-model', this['json-model']);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  rescoreElement (node) {
 | 
	
		
			
				|  |  | -    this.sinkDown(this.content.indexOf(node));
 | 
	
		
			
				|  |  | +    // THREE.ObjectLoader
 | 
	
		
			
				|  |  | +    if (!AFRAME.components['object-model']) {
 | 
	
		
			
				|  |  | +      AFRAME.registerComponent('object-model', this['object-model']);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    this._registered = true;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  sinkDown (n) {
 | 
	
		
			
				|  |  | -    // Fetch the element that has to be sunk.
 | 
	
		
			
				|  |  | -    const element = this.content[n];
 | 
	
		
			
				|  |  | +},{"./animation-mixer":94,"./fbx-model":95,"./gltf-model-legacy":96,"./json-model":98,"./object-model":99,"./ply-model":100}],98:[function(require,module,exports){
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * json-model
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Loader for THREE.js JSON format. Somewhat confusingly, there are two different THREE.js formats,
 | 
	
		
			
				|  |  | + * both having the .json extension. This loader supports only THREE.JsonLoader, which typically
 | 
	
		
			
				|  |  | + * includes only a single mesh.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Check the console for errors, if in doubt. You may need to use `object-model` or
 | 
	
		
			
				|  |  | + * `blend-character-model` for some .js and .json files.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * See: https://clara.io/learn/user-guide/data_exchange/threejs_export
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +module.exports = {
 | 
	
		
			
				|  |  | +  schema: {
 | 
	
		
			
				|  |  | +    src:         { type: 'asset' },
 | 
	
		
			
				|  |  | +    crossorigin: { default: '' }
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // When at 0, an element can not sink any further.
 | 
	
		
			
				|  |  | -    while (n > 0) {
 | 
	
		
			
				|  |  | -      // Compute the parent element's index, and fetch it.
 | 
	
		
			
				|  |  | -      const parentN = ((n + 1) >> 1) - 1;
 | 
	
		
			
				|  |  | -      const parent = this.content[parentN];
 | 
	
		
			
				|  |  | +  init: function () {
 | 
	
		
			
				|  |  | +    this.model = null;
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      if (this.scoreFunction(element) < this.scoreFunction(parent)) {
 | 
	
		
			
				|  |  | -        // Swap the elements if the parent is greater.
 | 
	
		
			
				|  |  | -        this.content[parentN] = element;
 | 
	
		
			
				|  |  | -        this.content[n] = parent;
 | 
	
		
			
				|  |  | -        // Update 'n' to continue at the new position.
 | 
	
		
			
				|  |  | -        n = parentN;
 | 
	
		
			
				|  |  | -      } else {
 | 
	
		
			
				|  |  | -        // Found a parent that is less, no need to sink any further.
 | 
	
		
			
				|  |  | -        break;
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +  update: function () {
 | 
	
		
			
				|  |  | +    var loader,
 | 
	
		
			
				|  |  | +        data = this.data;
 | 
	
		
			
				|  |  | +    if (!data.src) return;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  bubbleUp (n) {
 | 
	
		
			
				|  |  | -    // Look up the target element and its score.
 | 
	
		
			
				|  |  | -    const length = this.content.length,
 | 
	
		
			
				|  |  | -      element = this.content[n],
 | 
	
		
			
				|  |  | -      elemScore = this.scoreFunction(element);
 | 
	
		
			
				|  |  | +    this.remove();
 | 
	
		
			
				|  |  | +    loader = new THREE.JSONLoader();
 | 
	
		
			
				|  |  | +    if (data.crossorigin) loader.crossOrigin = data.crossorigin;
 | 
	
		
			
				|  |  | +    loader.load(data.src, function (geometry, materials) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    while (true) {
 | 
	
		
			
				|  |  | -      // Compute the indices of the child elements.
 | 
	
		
			
				|  |  | -      const child2N = (n + 1) << 1,
 | 
	
		
			
				|  |  | -        child1N = child2N - 1;
 | 
	
		
			
				|  |  | -      // This is used to store the new position of the element,
 | 
	
		
			
				|  |  | -      // if any.
 | 
	
		
			
				|  |  | -      let swap = null;
 | 
	
		
			
				|  |  | -      let child1Score;
 | 
	
		
			
				|  |  | -      // If the first child exists (is inside the array)...
 | 
	
		
			
				|  |  | -      if (child1N < length) {
 | 
	
		
			
				|  |  | -        // Look it up and compute its score.
 | 
	
		
			
				|  |  | -        const child1 = this.content[child1N];
 | 
	
		
			
				|  |  | -        child1Score = this.scoreFunction(child1);
 | 
	
		
			
				|  |  | +      // Attempt to automatically detect common material options.
 | 
	
		
			
				|  |  | +      materials.forEach(function (mat) {
 | 
	
		
			
				|  |  | +        mat.vertexColors = (geometry.faces[0] || {}).color ? THREE.FaceColors : THREE.NoColors;
 | 
	
		
			
				|  |  | +        mat.skinning = !!(geometry.bones || []).length;
 | 
	
		
			
				|  |  | +        mat.morphTargets = !!(geometry.morphTargets || []).length;
 | 
	
		
			
				|  |  | +        mat.morphNormals = !!(geometry.morphNormals || []).length;
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        // If the score is less than our element's, we need to swap.
 | 
	
		
			
				|  |  | -        if (child1Score < elemScore) {
 | 
	
		
			
				|  |  | -          swap = child1N;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | +      var model = (geometry.bones || []).length
 | 
	
		
			
				|  |  | +        ? new THREE.SkinnedMesh(geometry, new THREE.MultiMaterial(materials))
 | 
	
		
			
				|  |  | +        : new THREE.Mesh(geometry, new THREE.MultiMaterial(materials));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      // Do the same checks for the other child.
 | 
	
		
			
				|  |  | -      if (child2N < length) {
 | 
	
		
			
				|  |  | -        const child2 = this.content[child2N],
 | 
	
		
			
				|  |  | -          child2Score = this.scoreFunction(child2);
 | 
	
		
			
				|  |  | -        if (child2Score < (swap === null ? elemScore : child1Score)) {
 | 
	
		
			
				|  |  | -          swap = child2N;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | +      this.load(model);
 | 
	
		
			
				|  |  | +    }.bind(this));
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      // If the element needs to be moved, swap it, and continue.
 | 
	
		
			
				|  |  | -      if (swap !== null) {
 | 
	
		
			
				|  |  | -        this.content[n] = this.content[swap];
 | 
	
		
			
				|  |  | -        this.content[swap] = element;
 | 
	
		
			
				|  |  | -        n = swap;
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | +  load: function (model) {
 | 
	
		
			
				|  |  | +    this.model = model;
 | 
	
		
			
				|  |  | +    this.el.setObject3D('mesh', model);
 | 
	
		
			
				|  |  | +    this.el.emit('model-loaded', {format: 'json', model: model});
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      // Otherwise, we are done.
 | 
	
		
			
				|  |  | -      else {
 | 
	
		
			
				|  |  | -        break;
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +  remove: function () {
 | 
	
		
			
				|  |  | +    if (this.model) this.el.removeObject3D('mesh');
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | +},{}],99:[function(require,module,exports){
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * object-model
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Loader for THREE.js JSON format. Somewhat confusingly, there are two different THREE.js formats,
 | 
	
		
			
				|  |  | + * both having the .json extension. This loader supports only THREE.ObjectLoader, which typically
 | 
	
		
			
				|  |  | + * includes multiple meshes or an entire scene.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Check the console for errors, if in doubt. You may need to use `json-model` or
 | 
	
		
			
				|  |  | + * `blend-character-model` for some .js and .json files.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * See: https://clara.io/learn/user-guide/data_exchange/threejs_export
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +module.exports = {
 | 
	
		
			
				|  |  | +  schema: {
 | 
	
		
			
				|  |  | +    src:         { type: 'asset' },
 | 
	
		
			
				|  |  | +    crossorigin: { default: '' }
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -module.exports = BinaryHeap;
 | 
	
		
			
				|  |  | +  init: function () {
 | 
	
		
			
				|  |  | +    this.model = null;
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{}],118:[function(require,module,exports){
 | 
	
		
			
				|  |  | -const utils = require('./utils');
 | 
	
		
			
				|  |  | +  update: function () {
 | 
	
		
			
				|  |  | +    var loader,
 | 
	
		
			
				|  |  | +        data = this.data;
 | 
	
		
			
				|  |  | +    if (!data.src) return;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -class Channel {
 | 
	
		
			
				|  |  | -  constructor () {
 | 
	
		
			
				|  |  | -    this.portals = [];
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +    this.remove();
 | 
	
		
			
				|  |  | +    loader = new THREE.ObjectLoader();
 | 
	
		
			
				|  |  | +    if (data.crossorigin) loader.setCrossOrigin(data.crossorigin);
 | 
	
		
			
				|  |  | +    loader.load(data.src, function(object) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  push (p1, p2) {
 | 
	
		
			
				|  |  | -    if (p2 === undefined) p2 = p1;
 | 
	
		
			
				|  |  | -    this.portals.push({
 | 
	
		
			
				|  |  | -      left: p1,
 | 
	
		
			
				|  |  | -      right: p2
 | 
	
		
			
				|  |  | -    });
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +      // Enable skinning, if applicable.
 | 
	
		
			
				|  |  | +      object.traverse(function(o) {
 | 
	
		
			
				|  |  | +        if (o instanceof THREE.SkinnedMesh && o.material) {
 | 
	
		
			
				|  |  | +          o.material.skinning = !!((o.geometry && o.geometry.bones) || []).length;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  stringPull () {
 | 
	
		
			
				|  |  | -    const portals = this.portals;
 | 
	
		
			
				|  |  | -    const pts = [];
 | 
	
		
			
				|  |  | -    // Init scan state
 | 
	
		
			
				|  |  | -    let portalApex, portalLeft, portalRight;
 | 
	
		
			
				|  |  | -    let apexIndex = 0,
 | 
	
		
			
				|  |  | -      leftIndex = 0,
 | 
	
		
			
				|  |  | -      rightIndex = 0;
 | 
	
		
			
				|  |  | +      this.load(object);
 | 
	
		
			
				|  |  | +    }.bind(this));
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    portalApex = portals[0].left;
 | 
	
		
			
				|  |  | -    portalLeft = portals[0].left;
 | 
	
		
			
				|  |  | -    portalRight = portals[0].right;
 | 
	
		
			
				|  |  | +  load: function (model) {
 | 
	
		
			
				|  |  | +    this.model = model;
 | 
	
		
			
				|  |  | +    this.el.setObject3D('mesh', model);
 | 
	
		
			
				|  |  | +    this.el.emit('model-loaded', {format: 'json', model: model});
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // Add start point.
 | 
	
		
			
				|  |  | -    pts.push(portalApex);
 | 
	
		
			
				|  |  | +  remove: function () {
 | 
	
		
			
				|  |  | +    if (this.model) this.el.removeObject3D('mesh');
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    for (let i = 1; i < portals.length; i++) {
 | 
	
		
			
				|  |  | -      const left = portals[i].left;
 | 
	
		
			
				|  |  | -      const right = portals[i].right;
 | 
	
		
			
				|  |  | +},{}],100:[function(require,module,exports){
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * ply-model
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Wraps THREE.PLYLoader.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +THREE.PLYLoader = require('../../lib/PLYLoader');
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      // Update right vertex.
 | 
	
		
			
				|  |  | -      if (utils.triarea2(portalApex, portalRight, right) <= 0.0) {
 | 
	
		
			
				|  |  | -        if (utils.vequal(portalApex, portalRight) || utils.triarea2(portalApex, portalLeft, right) > 0.0) {
 | 
	
		
			
				|  |  | -          // Tighten the funnel.
 | 
	
		
			
				|  |  | -          portalRight = right;
 | 
	
		
			
				|  |  | -          rightIndex = i;
 | 
	
		
			
				|  |  | -        } else {
 | 
	
		
			
				|  |  | -          // Right over left, insert left to path and restart scan from portal left point.
 | 
	
		
			
				|  |  | -          pts.push(portalLeft);
 | 
	
		
			
				|  |  | -          // Make current left the new apex.
 | 
	
		
			
				|  |  | -          portalApex = portalLeft;
 | 
	
		
			
				|  |  | -          apexIndex = leftIndex;
 | 
	
		
			
				|  |  | -          // Reset portal
 | 
	
		
			
				|  |  | -          portalLeft = portalApex;
 | 
	
		
			
				|  |  | -          portalRight = portalApex;
 | 
	
		
			
				|  |  | -          leftIndex = apexIndex;
 | 
	
		
			
				|  |  | -          rightIndex = apexIndex;
 | 
	
		
			
				|  |  | -          // Restart scan
 | 
	
		
			
				|  |  | -          i = apexIndex;
 | 
	
		
			
				|  |  | -          continue;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Loads, caches, resolves geometries.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * @member cache - Promises that resolve geometries keyed by `src`.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +module.exports.System = {
 | 
	
		
			
				|  |  | +  init: function () {
 | 
	
		
			
				|  |  | +    this.cache = {};
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      // Update left vertex.
 | 
	
		
			
				|  |  | -      if (utils.triarea2(portalApex, portalLeft, left) >= 0.0) {
 | 
	
		
			
				|  |  | -        if (utils.vequal(portalApex, portalLeft) || utils.triarea2(portalApex, portalRight, left) < 0.0) {
 | 
	
		
			
				|  |  | -          // Tighten the funnel.
 | 
	
		
			
				|  |  | -          portalLeft = left;
 | 
	
		
			
				|  |  | -          leftIndex = i;
 | 
	
		
			
				|  |  | -        } else {
 | 
	
		
			
				|  |  | -          // Left over right, insert right to path and restart scan from portal right point.
 | 
	
		
			
				|  |  | -          pts.push(portalRight);
 | 
	
		
			
				|  |  | -          // Make current right the new apex.
 | 
	
		
			
				|  |  | -          portalApex = portalRight;
 | 
	
		
			
				|  |  | -          apexIndex = rightIndex;
 | 
	
		
			
				|  |  | -          // Reset portal
 | 
	
		
			
				|  |  | -          portalLeft = portalApex;
 | 
	
		
			
				|  |  | -          portalRight = portalApex;
 | 
	
		
			
				|  |  | -          leftIndex = apexIndex;
 | 
	
		
			
				|  |  | -          rightIndex = apexIndex;
 | 
	
		
			
				|  |  | -          // Restart scan
 | 
	
		
			
				|  |  | -          i = apexIndex;
 | 
	
		
			
				|  |  | -          continue;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * @returns {Promise}
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  getOrLoadGeometry: function (src, skipCache) {
 | 
	
		
			
				|  |  | +    var cache = this.cache;
 | 
	
		
			
				|  |  | +    var cacheItem = cache[src];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if ((pts.length === 0) || (!utils.vequal(pts[pts.length - 1], portals[portals.length - 1].left))) {
 | 
	
		
			
				|  |  | -      // Append last point to path.
 | 
	
		
			
				|  |  | -      pts.push(portals[portals.length - 1].left);
 | 
	
		
			
				|  |  | +    if (!skipCache && cacheItem) {
 | 
	
		
			
				|  |  | +      return cacheItem;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    this.path = pts;
 | 
	
		
			
				|  |  | -    return pts;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | +    cache[src] = new Promise(function (resolve) {
 | 
	
		
			
				|  |  | +      var loader = new THREE.PLYLoader();
 | 
	
		
			
				|  |  | +      loader.load(src, function (geometry) {
 | 
	
		
			
				|  |  | +        resolve(geometry);
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +    return cache[src];
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -module.exports = Channel;
 | 
	
		
			
				|  |  | +module.exports.Component = {
 | 
	
		
			
				|  |  | +  schema: {
 | 
	
		
			
				|  |  | +    skipCache: {type: 'boolean', default: false},
 | 
	
		
			
				|  |  | +    src: {type: 'asset'}
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{"./utils":120}],119:[function(require,module,exports){
 | 
	
		
			
				|  |  | -const utils = require('./utils');
 | 
	
		
			
				|  |  | -const AStar = require('./AStar');
 | 
	
		
			
				|  |  | -const Channel = require('./Channel');
 | 
	
		
			
				|  |  | +  init: function () {
 | 
	
		
			
				|  |  | +    this.model = null;
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -var polygonId = 1;
 | 
	
		
			
				|  |  | +  update: function () {
 | 
	
		
			
				|  |  | +    var data = this.data;
 | 
	
		
			
				|  |  | +    var el = this.el;
 | 
	
		
			
				|  |  | +    var loader;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -var buildPolygonGroups = function (navigationMesh) {
 | 
	
		
			
				|  |  | +    if (!data.src) {
 | 
	
		
			
				|  |  | +      console.warn('[%s] `src` property is required.', this.name);
 | 
	
		
			
				|  |  | +      return;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	var polygons = navigationMesh.polygons;
 | 
	
		
			
				|  |  | +    // Get geometry from system, create and set mesh.
 | 
	
		
			
				|  |  | +    this.system.getOrLoadGeometry(data.src, data.skipCache).then(function (geometry) {
 | 
	
		
			
				|  |  | +      var model = createModel(geometry);
 | 
	
		
			
				|  |  | +      el.setObject3D('mesh', model);
 | 
	
		
			
				|  |  | +      el.emit('model-loaded', {format: 'ply', model: model});
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	var polygonGroups = [];
 | 
	
		
			
				|  |  | -	var groupCount = 0;
 | 
	
		
			
				|  |  | +  remove: function () {
 | 
	
		
			
				|  |  | +    if (this.model) { this.el.removeObject3D('mesh'); }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	var spreadGroupId = function (polygon) {
 | 
	
		
			
				|  |  | -		polygon.neighbours.forEach((neighbour) => {
 | 
	
		
			
				|  |  | -			if (neighbour.group === undefined) {
 | 
	
		
			
				|  |  | -				neighbour.group = polygon.group;
 | 
	
		
			
				|  |  | -				spreadGroupId(neighbour);
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -		});
 | 
	
		
			
				|  |  | -	};
 | 
	
		
			
				|  |  | +function createModel (geometry) {
 | 
	
		
			
				|  |  | +  return new THREE.Mesh(geometry, new THREE.MeshPhongMaterial({
 | 
	
		
			
				|  |  | +    color: 0xFFFFFF,
 | 
	
		
			
				|  |  | +    shading: THREE.FlatShading,
 | 
	
		
			
				|  |  | +    vertexColors: THREE.VertexColors,
 | 
	
		
			
				|  |  | +    shininess: 0
 | 
	
		
			
				|  |  | +  }));
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	polygons.forEach((polygon) => {
 | 
	
		
			
				|  |  | +},{"../../lib/PLYLoader":6}],101:[function(require,module,exports){
 | 
	
		
			
				|  |  | +module.exports = {
 | 
	
		
			
				|  |  | +  schema: {
 | 
	
		
			
				|  |  | +    offset: {default: {x: 0, y: 0, z: 0}, type: 'vec3'}
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		if (polygon.group === undefined) {
 | 
	
		
			
				|  |  | -			polygon.group = groupCount++;
 | 
	
		
			
				|  |  | -			// Spread it
 | 
	
		
			
				|  |  | -			spreadGroupId(polygon);
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | +  init: function () {
 | 
	
		
			
				|  |  | +    this.active = false;
 | 
	
		
			
				|  |  | +    this.targetEl = null;
 | 
	
		
			
				|  |  | +    this.fire = this.fire.bind(this);
 | 
	
		
			
				|  |  | +    this.offset = new THREE.Vector3();
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		if (!polygonGroups[polygon.group]) polygonGroups[polygon.group] = [];
 | 
	
		
			
				|  |  | +  update: function () {
 | 
	
		
			
				|  |  | +    this.offset.copy(this.data.offset);
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		polygonGroups[polygon.group].push(polygon);
 | 
	
		
			
				|  |  | -	});
 | 
	
		
			
				|  |  | +  play: function () { this.el.addEventListener('click', this.fire); },
 | 
	
		
			
				|  |  | +  pause: function () { this.el.removeEventListener('click', this.fire); },
 | 
	
		
			
				|  |  | +  remove: function () { this.pause(); },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	console.log('Groups built: ', polygonGroups.length);
 | 
	
		
			
				|  |  | +  fire: function () {
 | 
	
		
			
				|  |  | +    var targetEl = this.el.sceneEl.querySelector('[checkpoint-controls]');
 | 
	
		
			
				|  |  | +    if (!targetEl) {
 | 
	
		
			
				|  |  | +      throw new Error('No `checkpoint-controls` component found.');
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    targetEl.components['checkpoint-controls'].setCheckpoint(this.el);
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	return polygonGroups;
 | 
	
		
			
				|  |  | +  getOffset: function () {
 | 
	
		
			
				|  |  | +    return this.offset.copy(this.data.offset);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -var buildPolygonNeighbours = function (polygon, navigationMesh) {
 | 
	
		
			
				|  |  | -	polygon.neighbours = [];
 | 
	
		
			
				|  |  | +},{}],102:[function(require,module,exports){
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Specifies an envMap on an entity, without replacing any existing material
 | 
	
		
			
				|  |  | + * properties.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +module.exports = {
 | 
	
		
			
				|  |  | +  schema: {
 | 
	
		
			
				|  |  | +    path: {default: ''},
 | 
	
		
			
				|  |  | +    extension: {default: 'jpg'},
 | 
	
		
			
				|  |  | +    format: {default: 'RGBFormat'},
 | 
	
		
			
				|  |  | +    enableBackground: {default: false}
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	// All other nodes that contain at least two of our vertices are our neighbours
 | 
	
		
			
				|  |  | -	for (var i = 0, len = navigationMesh.polygons.length; i < len; i++) {
 | 
	
		
			
				|  |  | -		if (polygon === navigationMesh.polygons[i]) continue;
 | 
	
		
			
				|  |  | +  init: function () {
 | 
	
		
			
				|  |  | +    var data = this.data;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		// Don't check polygons that are too far, since the intersection tests take a long time
 | 
	
		
			
				|  |  | -		if (polygon.centroid.distanceToSquared(navigationMesh.polygons[i].centroid) > 100 * 100) continue;
 | 
	
		
			
				|  |  | +    this.texture = new THREE.CubeTextureLoader().load([
 | 
	
		
			
				|  |  | +      data.path + 'posx.' + data.extension, data.path + 'negx.' + data.extension,
 | 
	
		
			
				|  |  | +      data.path + 'posy.' + data.extension, data.path + 'negy.' + data.extension,
 | 
	
		
			
				|  |  | +      data.path + 'posz.' + data.extension, data.path + 'negz.' + data.extension
 | 
	
		
			
				|  |  | +    ]);
 | 
	
		
			
				|  |  | +    this.texture.format = THREE[data.format];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		var matches = utils.array_intersect(polygon.vertexIds, navigationMesh.polygons[i].vertexIds);
 | 
	
		
			
				|  |  | +    if (data.enableBackground) {
 | 
	
		
			
				|  |  | +      this.el.sceneEl.object3D.background = this.texture;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		if (matches.length >= 2) {
 | 
	
		
			
				|  |  | -			polygon.neighbours.push(navigationMesh.polygons[i]);
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | +    this.applyEnvMap();
 | 
	
		
			
				|  |  | +    this.el.addEventListener('object3dset', this.applyEnvMap.bind(this));
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  applyEnvMap: function () {
 | 
	
		
			
				|  |  | +    var mesh = this.el.getObject3D('mesh');
 | 
	
		
			
				|  |  | +    var envMap = this.texture;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (!mesh) return;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    mesh.traverse(function (node) {
 | 
	
		
			
				|  |  | +      if (node.material && 'envMap' in node.material) {
 | 
	
		
			
				|  |  | +        node.material.envMap = envMap;
 | 
	
		
			
				|  |  | +        node.material.needsUpdate = true;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -var buildPolygonsFromGeometry = function (geometry) {
 | 
	
		
			
				|  |  | +},{}],103:[function(require,module,exports){
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Based on aframe/examples/showcase/tracked-controls.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Handles events coming from the hand-controls.
 | 
	
		
			
				|  |  | + * Determines if the entity is grabbed or released.
 | 
	
		
			
				|  |  | + * Updates its position to move along the controller.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +module.exports = {
 | 
	
		
			
				|  |  | +  init: function () {
 | 
	
		
			
				|  |  | +    this.GRABBED_STATE = 'grabbed';
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	console.log('Vertices:', geometry.vertices.length, 'polygons:', geometry.faces.length);
 | 
	
		
			
				|  |  | +    this.grabbing = false;
 | 
	
		
			
				|  |  | +    this.hitEl =      /** @type {AFRAME.Element}    */ null;
 | 
	
		
			
				|  |  | +    this.physics =    /** @type {AFRAME.System}     */ this.el.sceneEl.systems.physics;
 | 
	
		
			
				|  |  | +    this.constraint = /** @type {CANNON.Constraint} */ null;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	var polygons = [];
 | 
	
		
			
				|  |  | -	var vertices = geometry.vertices;
 | 
	
		
			
				|  |  | -	var faceVertexUvs = geometry.faceVertexUvs;
 | 
	
		
			
				|  |  | +    // Bind event handlers
 | 
	
		
			
				|  |  | +    this.onHit = this.onHit.bind(this);
 | 
	
		
			
				|  |  | +    this.onGripOpen = this.onGripOpen.bind(this);
 | 
	
		
			
				|  |  | +    this.onGripClose = this.onGripClose.bind(this);
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	// Convert the faces into a custom format that supports more than 3 vertices
 | 
	
		
			
				|  |  | -	geometry.faces.forEach((face) => {
 | 
	
		
			
				|  |  | -		polygons.push({
 | 
	
		
			
				|  |  | -			id: polygonId++,
 | 
	
		
			
				|  |  | -			vertexIds: [face.a, face.b, face.c],
 | 
	
		
			
				|  |  | -			centroid: face.centroid,
 | 
	
		
			
				|  |  | -			normal: face.normal,
 | 
	
		
			
				|  |  | -			neighbours: []
 | 
	
		
			
				|  |  | -		});
 | 
	
		
			
				|  |  | -	});
 | 
	
		
			
				|  |  | +  play: function () {
 | 
	
		
			
				|  |  | +    var el = this.el;
 | 
	
		
			
				|  |  | +    el.addEventListener('hit', this.onHit);
 | 
	
		
			
				|  |  | +    el.addEventListener('gripdown', this.onGripClose);
 | 
	
		
			
				|  |  | +    el.addEventListener('gripup', this.onGripOpen);
 | 
	
		
			
				|  |  | +    el.addEventListener('trackpaddown', this.onGripClose);
 | 
	
		
			
				|  |  | +    el.addEventListener('trackpadup', this.onGripOpen);
 | 
	
		
			
				|  |  | +    el.addEventListener('triggerdown', this.onGripClose);
 | 
	
		
			
				|  |  | +    el.addEventListener('triggerup', this.onGripOpen);
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	var navigationMesh = {
 | 
	
		
			
				|  |  | -		polygons: polygons,
 | 
	
		
			
				|  |  | -		vertices: vertices,
 | 
	
		
			
				|  |  | -		faceVertexUvs: faceVertexUvs
 | 
	
		
			
				|  |  | -	};
 | 
	
		
			
				|  |  | +  pause: function () {
 | 
	
		
			
				|  |  | +    var el = this.el;
 | 
	
		
			
				|  |  | +    el.removeEventListener('hit', this.onHit);
 | 
	
		
			
				|  |  | +    el.removeEventListener('gripdown', this.onGripClose);
 | 
	
		
			
				|  |  | +    el.removeEventListener('gripup', this.onGripOpen);
 | 
	
		
			
				|  |  | +    el.removeEventListener('trackpaddown', this.onGripClose);
 | 
	
		
			
				|  |  | +    el.removeEventListener('trackpadup', this.onGripOpen);
 | 
	
		
			
				|  |  | +    el.removeEventListener('triggerdown', this.onGripClose);
 | 
	
		
			
				|  |  | +    el.removeEventListener('triggerup', this.onGripOpen);
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	// Build a list of adjacent polygons
 | 
	
		
			
				|  |  | -	polygons.forEach((polygon) => {
 | 
	
		
			
				|  |  | -		buildPolygonNeighbours(polygon, navigationMesh);
 | 
	
		
			
				|  |  | -	});
 | 
	
		
			
				|  |  | +  onGripClose: function (evt) {
 | 
	
		
			
				|  |  | +    this.grabbing = true;
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	return navigationMesh;
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | +  onGripOpen: function (evt) {
 | 
	
		
			
				|  |  | +    var hitEl = this.hitEl;
 | 
	
		
			
				|  |  | +    this.grabbing = false;
 | 
	
		
			
				|  |  | +    if (!hitEl) { return; }
 | 
	
		
			
				|  |  | +    hitEl.removeState(this.GRABBED_STATE);
 | 
	
		
			
				|  |  | +    this.hitEl = undefined;
 | 
	
		
			
				|  |  | +    this.physics.world.removeConstraint(this.constraint);
 | 
	
		
			
				|  |  | +    this.constraint = null;
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -var buildNavigationMesh = function (geometry) {
 | 
	
		
			
				|  |  | -	// Prepare geometry
 | 
	
		
			
				|  |  | -	utils.computeCentroids(geometry);
 | 
	
		
			
				|  |  | -	geometry.mergeVertices();
 | 
	
		
			
				|  |  | -	return buildPolygonsFromGeometry(geometry);
 | 
	
		
			
				|  |  | +  onHit: function (evt) {
 | 
	
		
			
				|  |  | +    var hitEl = evt.detail.el;
 | 
	
		
			
				|  |  | +    // If the element is already grabbed (it could be grabbed by another controller).
 | 
	
		
			
				|  |  | +    // If the hand is not grabbing the element does not stick.
 | 
	
		
			
				|  |  | +    // If we're already grabbing something you can't grab again.
 | 
	
		
			
				|  |  | +    if (!hitEl || hitEl.is(this.GRABBED_STATE) || !this.grabbing || this.hitEl) { return; }
 | 
	
		
			
				|  |  | +    hitEl.addState(this.GRABBED_STATE);
 | 
	
		
			
				|  |  | +    this.hitEl = hitEl;
 | 
	
		
			
				|  |  | +    this.constraint = new CANNON.LockConstraint(this.el.body, hitEl.body);
 | 
	
		
			
				|  |  | +    this.physics.world.addConstraint(this.constraint);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -var getSharedVerticesInOrder = function (a, b) {
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -	var aList = a.vertexIds;
 | 
	
		
			
				|  |  | -	var bList = b.vertexIds;
 | 
	
		
			
				|  |  | +},{}],104:[function(require,module,exports){
 | 
	
		
			
				|  |  | +var physics = require('aframe-physics-system');
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	var sharedVertices = [];
 | 
	
		
			
				|  |  | +module.exports = {
 | 
	
		
			
				|  |  | +  'checkpoint':      require('./checkpoint'),
 | 
	
		
			
				|  |  | +  'cube-env-map':    require('./cube-env-map'),
 | 
	
		
			
				|  |  | +  'grab':            require('./grab'),
 | 
	
		
			
				|  |  | +  'jump-ability':    require('./jump-ability'),
 | 
	
		
			
				|  |  | +  'kinematic-body':  require('./kinematic-body'),
 | 
	
		
			
				|  |  | +  'mesh-smooth':     require('./mesh-smooth'),
 | 
	
		
			
				|  |  | +  'sphere-collider': require('./sphere-collider'),
 | 
	
		
			
				|  |  | +  'toggle-velocity': require('./toggle-velocity'),
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	aList.forEach((vId) => {
 | 
	
		
			
				|  |  | -		if (bList.includes(vId)) {
 | 
	
		
			
				|  |  | -			sharedVertices.push(vId);
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | -	});
 | 
	
		
			
				|  |  | +  registerAll: function (AFRAME) {
 | 
	
		
			
				|  |  | +    if (this._registered) return;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	if (sharedVertices.length < 2) return [];
 | 
	
		
			
				|  |  | +    AFRAME = AFRAME || window.AFRAME;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	// console.log("TRYING aList:", aList, ", bList:", bList, ", sharedVertices:", sharedVertices);
 | 
	
		
			
				|  |  | +    physics.registerAll();
 | 
	
		
			
				|  |  | +    if (!AFRAME.components['checkpoint'])      AFRAME.registerComponent('checkpoint',      this['checkpoint']);
 | 
	
		
			
				|  |  | +    if (!AFRAME.components['cube-env-map'])    AFRAME.registerComponent('cube-env-map',    this['cube-env-map']);
 | 
	
		
			
				|  |  | +    if (!AFRAME.components['grab'])            AFRAME.registerComponent('grab',            this['grab']);
 | 
	
		
			
				|  |  | +    if (!AFRAME.components['jump-ability'])    AFRAME.registerComponent('jump-ability',    this['jump-ability']);
 | 
	
		
			
				|  |  | +    if (!AFRAME.components['kinematic-body'])  AFRAME.registerComponent('kinematic-body',  this['kinematic-body']);
 | 
	
		
			
				|  |  | +    if (!AFRAME.components['mesh-smooth'])     AFRAME.registerComponent('mesh-smooth',     this['mesh-smooth']);
 | 
	
		
			
				|  |  | +    if (!AFRAME.components['sphere-collider']) AFRAME.registerComponent('sphere-collider', this['sphere-collider']);
 | 
	
		
			
				|  |  | +    if (!AFRAME.components['toggle-velocity']) AFRAME.registerComponent('toggle-velocity', this['toggle-velocity']);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	if (sharedVertices.includes(aList[0]) && sharedVertices.includes(aList[aList.length - 1])) {
 | 
	
		
			
				|  |  | -		// Vertices on both edges are bad, so shift them once to the left
 | 
	
		
			
				|  |  | -		aList.push(aList.shift());
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | +    this._registered = true;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	if (sharedVertices.includes(bList[0]) && sharedVertices.includes(bList[bList.length - 1])) {
 | 
	
		
			
				|  |  | -		// Vertices on both edges are bad, so shift them once to the left
 | 
	
		
			
				|  |  | -		bList.push(bList.shift());
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | +},{"./checkpoint":101,"./cube-env-map":102,"./grab":103,"./jump-ability":105,"./kinematic-body":106,"./mesh-smooth":107,"./sphere-collider":108,"./toggle-velocity":109,"aframe-physics-system":11}],105:[function(require,module,exports){
 | 
	
		
			
				|  |  | +var ACCEL_G = -9.8, // m/s^2
 | 
	
		
			
				|  |  | +    EASING = -15; // m/s^2
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	// Again!
 | 
	
		
			
				|  |  | -	sharedVertices = [];
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Jump ability.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +module.exports = {
 | 
	
		
			
				|  |  | +  dependencies: ['velocity'],
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	aList.forEach((vId) => {
 | 
	
		
			
				|  |  | -		if (bList.includes(vId)) {
 | 
	
		
			
				|  |  | -			sharedVertices.push(vId);
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | -	});
 | 
	
		
			
				|  |  | +  /* Schema
 | 
	
		
			
				|  |  | +  ——————————————————————————————————————————————*/
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	return sharedVertices;
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | +  schema: {
 | 
	
		
			
				|  |  | +    on: { default: 'keydown:Space gamepadbuttondown:0' },
 | 
	
		
			
				|  |  | +    playerHeight: { default: 1.764 },
 | 
	
		
			
				|  |  | +    maxJumps: { default: 1 },
 | 
	
		
			
				|  |  | +    distance: { default: 5 },
 | 
	
		
			
				|  |  | +    soundJump: { default: '' },
 | 
	
		
			
				|  |  | +    soundLand: { default: '' },
 | 
	
		
			
				|  |  | +    debug: { default: false }
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -var groupNavMesh = function (navigationMesh) {
 | 
	
		
			
				|  |  | +  init: function () {
 | 
	
		
			
				|  |  | +    this.velocity = 0;
 | 
	
		
			
				|  |  | +    this.numJumps = 0;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	var saveObj = {};
 | 
	
		
			
				|  |  | +    var beginJump = this.beginJump.bind(this),
 | 
	
		
			
				|  |  | +        events = this.data.on.split(' ');
 | 
	
		
			
				|  |  | +    this.bindings = {};
 | 
	
		
			
				|  |  | +    for (var i = 0; i <  events.length; i++) {
 | 
	
		
			
				|  |  | +      this.bindings[events[i]] = beginJump;
 | 
	
		
			
				|  |  | +      this.el.addEventListener(events[i], beginJump);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    this.bindings.collide = this.onCollide.bind(this);
 | 
	
		
			
				|  |  | +    this.el.addEventListener('collide', this.bindings.collide);
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	navigationMesh.vertices.forEach((v) => {
 | 
	
		
			
				|  |  | -		v.x = utils.roundNumber(v.x, 2);
 | 
	
		
			
				|  |  | -		v.y = utils.roundNumber(v.y, 2);
 | 
	
		
			
				|  |  | -		v.z = utils.roundNumber(v.z, 2);
 | 
	
		
			
				|  |  | -	});
 | 
	
		
			
				|  |  | +  remove: function () {
 | 
	
		
			
				|  |  | +    for (var event in this.bindings) {
 | 
	
		
			
				|  |  | +      if (this.bindings.hasOwnProperty(event)) {
 | 
	
		
			
				|  |  | +        this.el.removeEventListener(event, this.bindings[event]);
 | 
	
		
			
				|  |  | +        delete this.bindings[event];
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    this.el.removeEventListener('collide', this.bindings.collide);
 | 
	
		
			
				|  |  | +    delete this.bindings.collide;
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	saveObj.vertices = navigationMesh.vertices;
 | 
	
		
			
				|  |  | +  beginJump: function () {
 | 
	
		
			
				|  |  | +    if (this.numJumps < this.data.maxJumps) {
 | 
	
		
			
				|  |  | +      var data = this.data,
 | 
	
		
			
				|  |  | +          initialVelocity = Math.sqrt(-2 * data.distance * (ACCEL_G + EASING)),
 | 
	
		
			
				|  |  | +          v = this.el.getAttribute('velocity');
 | 
	
		
			
				|  |  | +      this.el.setAttribute('velocity', {x: v.x, y: initialVelocity, z: v.z});
 | 
	
		
			
				|  |  | +      this.numJumps++;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	var groups = buildPolygonGroups(navigationMesh);
 | 
	
		
			
				|  |  | +  onCollide: function () {
 | 
	
		
			
				|  |  | +    this.numJumps = 0;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	saveObj.groups = [];
 | 
	
		
			
				|  |  | +},{}],106:[function(require,module,exports){
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Kinematic body.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Managed dynamic body, which moves but is not affected (directly) by the
 | 
	
		
			
				|  |  | + * physics engine. This is not a true kinematic body, in the sense that we are
 | 
	
		
			
				|  |  | + * letting the physics engine _compute_ collisions against it and selectively
 | 
	
		
			
				|  |  | + * applying those collisions to the object. The physics engine does not decide
 | 
	
		
			
				|  |  | + * the position/velocity/rotation of the element.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Used for the camera object, because full physics simulation would create
 | 
	
		
			
				|  |  | + * movement that feels unnatural to the player. Bipedal movement does not
 | 
	
		
			
				|  |  | + * translate nicely to rigid body physics.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * See: http://www.learn-cocos2d.com/2013/08/physics-engine-platformer-terrible-idea/
 | 
	
		
			
				|  |  | + * And: http://oxleygamedev.blogspot.com/2011/04/player-physics-part-2.html
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +var CANNON = window.CANNON;
 | 
	
		
			
				|  |  | +var EPS = 0.000001;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	var findPolygonIndex = function (group, p) {
 | 
	
		
			
				|  |  | -		for (var i = 0; i < group.length; i++) {
 | 
	
		
			
				|  |  | -			if (p === group[i]) return i;
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | -	};
 | 
	
		
			
				|  |  | +module.exports = {
 | 
	
		
			
				|  |  | +  dependencies: ['velocity'],
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	groups.forEach((group) => {
 | 
	
		
			
				|  |  | +  /*******************************************************************
 | 
	
		
			
				|  |  | +   * Schema
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		var newGroup = [];
 | 
	
		
			
				|  |  | +  schema: {
 | 
	
		
			
				|  |  | +    mass:           { default: 5 },
 | 
	
		
			
				|  |  | +    radius:         { default: 1.3 },
 | 
	
		
			
				|  |  | +    height:         { default: 1.764 },
 | 
	
		
			
				|  |  | +    linearDamping:  { default: 0.05 },
 | 
	
		
			
				|  |  | +    enableSlopes:   { default: true }
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		group.forEach((p) => {
 | 
	
		
			
				|  |  | +  /*******************************************************************
 | 
	
		
			
				|  |  | +   * Lifecycle
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -			var neighbours = [];
 | 
	
		
			
				|  |  | +  init: function () {
 | 
	
		
			
				|  |  | +    this.system = this.el.sceneEl.systems.physics;
 | 
	
		
			
				|  |  | +    this.system.addBehavior(this, this.system.Phase.SIMULATE);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -			p.neighbours.forEach((n) => {
 | 
	
		
			
				|  |  | -				neighbours.push(findPolygonIndex(group, n));
 | 
	
		
			
				|  |  | -			});
 | 
	
		
			
				|  |  | +    var el = this.el,
 | 
	
		
			
				|  |  | +        data = this.data,
 | 
	
		
			
				|  |  | +        position = (new CANNON.Vec3()).copy(el.getAttribute('position'));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    this.body = new CANNON.Body({
 | 
	
		
			
				|  |  | +      material: this.system.material,
 | 
	
		
			
				|  |  | +      position: position,
 | 
	
		
			
				|  |  | +      mass: data.mass,
 | 
	
		
			
				|  |  | +      linearDamping: data.linearDamping,
 | 
	
		
			
				|  |  | +      fixedRotation: true
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +    this.body.addShape(
 | 
	
		
			
				|  |  | +      new CANNON.Sphere(data.radius),
 | 
	
		
			
				|  |  | +      new CANNON.Vec3(0, data.radius - data.height, 0)
 | 
	
		
			
				|  |  | +    );
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -			// Build a portal list to each neighbour
 | 
	
		
			
				|  |  | -			var portals = [];
 | 
	
		
			
				|  |  | -			p.neighbours.forEach((n) => {
 | 
	
		
			
				|  |  | -				portals.push(getSharedVerticesInOrder(p, n));
 | 
	
		
			
				|  |  | -			});
 | 
	
		
			
				|  |  | +    this.body.el = this.el;
 | 
	
		
			
				|  |  | +    this.el.body = this.body;
 | 
	
		
			
				|  |  | +    this.system.addBody(this.body);
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  remove: function () {
 | 
	
		
			
				|  |  | +    this.system.removeBody(this.body);
 | 
	
		
			
				|  |  | +    this.system.removeBehavior(this, this.system.Phase.SIMULATE);
 | 
	
		
			
				|  |  | +    delete this.el.body;
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -			p.centroid.x = utils.roundNumber(p.centroid.x, 2);
 | 
	
		
			
				|  |  | -			p.centroid.y = utils.roundNumber(p.centroid.y, 2);
 | 
	
		
			
				|  |  | -			p.centroid.z = utils.roundNumber(p.centroid.z, 2);
 | 
	
		
			
				|  |  | +  /*******************************************************************
 | 
	
		
			
				|  |  | +   * Tick
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -			newGroup.push({
 | 
	
		
			
				|  |  | -				id: findPolygonIndex(group, p),
 | 
	
		
			
				|  |  | -				neighbours: neighbours,
 | 
	
		
			
				|  |  | -				vertexIds: p.vertexIds,
 | 
	
		
			
				|  |  | -				centroid: p.centroid,
 | 
	
		
			
				|  |  | -				portals: portals
 | 
	
		
			
				|  |  | -			});
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * Checks CANNON.World for collisions and attempts to apply them to the
 | 
	
		
			
				|  |  | +   * element automatically, in a player-friendly way.
 | 
	
		
			
				|  |  | +   *
 | 
	
		
			
				|  |  | +   * There's extra logic for horizontal surfaces here. The basic requirements:
 | 
	
		
			
				|  |  | +   * (1) Only apply gravity when not in contact with _any_ horizontal surface.
 | 
	
		
			
				|  |  | +   * (2) When moving, project the velocity against exactly one ground surface.
 | 
	
		
			
				|  |  | +   *     If in contact with two ground surfaces (e.g. ground + ramp), choose
 | 
	
		
			
				|  |  | +   *     the one that collides with current velocity, if any.
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  step: (function () {
 | 
	
		
			
				|  |  | +    var velocity = new THREE.Vector3(),
 | 
	
		
			
				|  |  | +        normalizedVelocity = new THREE.Vector3(),
 | 
	
		
			
				|  |  | +        currentSurfaceNormal = new THREE.Vector3(),
 | 
	
		
			
				|  |  | +        groundNormal = new THREE.Vector3();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		});
 | 
	
		
			
				|  |  | +    return function (t, dt) {
 | 
	
		
			
				|  |  | +      if (!dt) return;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		saveObj.groups.push(newGroup);
 | 
	
		
			
				|  |  | -	});
 | 
	
		
			
				|  |  | +      var body = this.body,
 | 
	
		
			
				|  |  | +          data = this.data,
 | 
	
		
			
				|  |  | +          didCollide = false,
 | 
	
		
			
				|  |  | +          height, groundHeight = -Infinity,
 | 
	
		
			
				|  |  | +          groundBody;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	return saveObj;
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | +      dt = Math.min(dt, this.system.data.maxInterval * 1000);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -var zoneNodes = {};
 | 
	
		
			
				|  |  | +      groundNormal.set(0, 0, 0);
 | 
	
		
			
				|  |  | +      velocity.copy(this.el.getAttribute('velocity'));
 | 
	
		
			
				|  |  | +      body.velocity.copy(velocity);
 | 
	
		
			
				|  |  | +      body.position.copy(this.el.getAttribute('position'));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -module.exports = {
 | 
	
		
			
				|  |  | -	buildNodes: function (geometry) {
 | 
	
		
			
				|  |  | -		var navigationMesh = buildNavigationMesh(geometry);
 | 
	
		
			
				|  |  | +      for (var i = 0, contact; (contact = this.system.world.contacts[i]); i++) {
 | 
	
		
			
				|  |  | +        // 1. Find any collisions involving this element. Get the contact
 | 
	
		
			
				|  |  | +        // normal, and make sure it's oriented _out_ of the other object and
 | 
	
		
			
				|  |  | +        // enabled (body.collisionReponse is true for both bodies)
 | 
	
		
			
				|  |  | +        if (!contact.enabled) { continue; }
 | 
	
		
			
				|  |  | +        if (body.id === contact.bi.id) {
 | 
	
		
			
				|  |  | +          contact.ni.negate(currentSurfaceNormal);
 | 
	
		
			
				|  |  | +        } else if (body.id === contact.bj.id) {
 | 
	
		
			
				|  |  | +          currentSurfaceNormal.copy(contact.ni);
 | 
	
		
			
				|  |  | +        } else {
 | 
	
		
			
				|  |  | +          continue;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		var zoneNodes = groupNavMesh(navigationMesh);
 | 
	
		
			
				|  |  | +        didCollide = body.velocity.dot(currentSurfaceNormal) < -EPS;
 | 
	
		
			
				|  |  | +        if (didCollide && currentSurfaceNormal.y <= 0.5) {
 | 
	
		
			
				|  |  | +          // 2. If current trajectory attempts to move _through_ another
 | 
	
		
			
				|  |  | +          // object, project the velocity against the collision plane to
 | 
	
		
			
				|  |  | +          // prevent passing through.
 | 
	
		
			
				|  |  | +          velocity = velocity.projectOnPlane(currentSurfaceNormal);
 | 
	
		
			
				|  |  | +        } else if (currentSurfaceNormal.y > 0.5) {
 | 
	
		
			
				|  |  | +          // 3. If in contact with something roughly horizontal (+/- 45º) then
 | 
	
		
			
				|  |  | +          // consider that the current ground. Only the highest qualifying
 | 
	
		
			
				|  |  | +          // ground is retained.
 | 
	
		
			
				|  |  | +          height = body.id === contact.bi.id
 | 
	
		
			
				|  |  | +            ? Math.abs(contact.rj.y + contact.bj.position.y)
 | 
	
		
			
				|  |  | +            : Math.abs(contact.ri.y + contact.bi.position.y);
 | 
	
		
			
				|  |  | +          if (height > groundHeight) {
 | 
	
		
			
				|  |  | +            groundHeight = height;
 | 
	
		
			
				|  |  | +            groundNormal.copy(currentSurfaceNormal);
 | 
	
		
			
				|  |  | +            groundBody = body.id === contact.bi.id ? contact.bj : contact.bi;
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		return zoneNodes;
 | 
	
		
			
				|  |  | -	},
 | 
	
		
			
				|  |  | -	setZoneData: function (zone, data) {
 | 
	
		
			
				|  |  | -		zoneNodes[zone] = data;
 | 
	
		
			
				|  |  | -	},
 | 
	
		
			
				|  |  | -	getGroup: function (zone, position) {
 | 
	
		
			
				|  |  | +      normalizedVelocity.copy(velocity).normalize();
 | 
	
		
			
				|  |  | +      if (groundBody && normalizedVelocity.y < 0.5) {
 | 
	
		
			
				|  |  | +        if (!data.enableSlopes) {
 | 
	
		
			
				|  |  | +          groundNormal.set(0, 1, 0);
 | 
	
		
			
				|  |  | +        } else if (groundNormal.y < 1 - EPS) {
 | 
	
		
			
				|  |  | +          groundNormal.copy(this.raycastToGround(groundBody, groundNormal));
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		if (!zoneNodes[zone]) return null;
 | 
	
		
			
				|  |  | +        // 4. Project trajectory onto the top-most ground object, unless
 | 
	
		
			
				|  |  | +        // trajectory is > 45º.
 | 
	
		
			
				|  |  | +        velocity = velocity.projectOnPlane(groundNormal);
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        // 5. If not in contact with anything horizontal, apply world gravity.
 | 
	
		
			
				|  |  | +        // TODO - Why is the 4x scalar necessary.
 | 
	
		
			
				|  |  | +        velocity.add(this.system.world.gravity.scale(dt * 4.0 / 1000));
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		var closestNodeGroup = null;
 | 
	
		
			
				|  |  | +      // 6. If the ground surface has a velocity, apply it directly to current
 | 
	
		
			
				|  |  | +      // position, not velocity, to preserve relative velocity.
 | 
	
		
			
				|  |  | +      if (groundBody && groundBody.el && groundBody.el.components.velocity) {
 | 
	
		
			
				|  |  | +        var groundVelocity = groundBody.el.getAttribute('velocity');
 | 
	
		
			
				|  |  | +        body.position.copy({
 | 
	
		
			
				|  |  | +          x: body.position.x + groundVelocity.x * dt / 1000,
 | 
	
		
			
				|  |  | +          y: body.position.y + groundVelocity.y * dt / 1000,
 | 
	
		
			
				|  |  | +          z: body.position.z + groundVelocity.z * dt / 1000
 | 
	
		
			
				|  |  | +        });
 | 
	
		
			
				|  |  | +        this.el.setAttribute('position', body.position);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		var distance = Math.pow(50, 2);
 | 
	
		
			
				|  |  | +      body.velocity.copy(velocity);
 | 
	
		
			
				|  |  | +      this.el.setAttribute('velocity', velocity);
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +  }()),
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		zoneNodes[zone].groups.forEach((group, index) => {
 | 
	
		
			
				|  |  | -			group.forEach((node) => {
 | 
	
		
			
				|  |  | -				var measuredDistance = utils.distanceToSquared(node.centroid, position);
 | 
	
		
			
				|  |  | -				if (measuredDistance < distance) {
 | 
	
		
			
				|  |  | -					closestNodeGroup = index;
 | 
	
		
			
				|  |  | -					distance = measuredDistance;
 | 
	
		
			
				|  |  | -				}
 | 
	
		
			
				|  |  | -			});
 | 
	
		
			
				|  |  | -		});
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * When walking on complex surfaces (trimeshes, borders between two shapes),
 | 
	
		
			
				|  |  | +   * the collision normals returned for the player sphere can be very
 | 
	
		
			
				|  |  | +   * inconsistent. To address this, raycast straight down, find the collision
 | 
	
		
			
				|  |  | +   * normal, and return whichever normal is more vertical.
 | 
	
		
			
				|  |  | +   * @param  {CANNON.Body} groundBody
 | 
	
		
			
				|  |  | +   * @param  {CANNON.Vec3} groundNormal
 | 
	
		
			
				|  |  | +   * @return {CANNON.Vec3}
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  raycastToGround: function (groundBody, groundNormal) {
 | 
	
		
			
				|  |  | +    var ray,
 | 
	
		
			
				|  |  | +        hitNormal,
 | 
	
		
			
				|  |  | +        vFrom = this.body.position,
 | 
	
		
			
				|  |  | +        vTo = this.body.position.clone();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		return closestNodeGroup;
 | 
	
		
			
				|  |  | -	},
 | 
	
		
			
				|  |  | -	getRandomNode: function (zone, group, nearPosition, nearRange) {
 | 
	
		
			
				|  |  | +    vTo.y -= this.data.height;
 | 
	
		
			
				|  |  | +    ray = new CANNON.Ray(vFrom, vTo);
 | 
	
		
			
				|  |  | +    ray._updateDirection(); // TODO - Report bug.
 | 
	
		
			
				|  |  | +    ray.intersectBody(groundBody);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		if (!zoneNodes[zone]) return new THREE.Vector3();
 | 
	
		
			
				|  |  | +    if (!ray.hasHit) return groundNormal;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		nearPosition = nearPosition || null;
 | 
	
		
			
				|  |  | -		nearRange = nearRange || 0;
 | 
	
		
			
				|  |  | +    // Compare ABS, in case we're projecting against the inside of the face.
 | 
	
		
			
				|  |  | +    hitNormal = ray.result.hitNormalWorld;
 | 
	
		
			
				|  |  | +    return Math.abs(hitNormal.y) > Math.abs(groundNormal.y) ? hitNormal : groundNormal;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		var candidates = [];
 | 
	
		
			
				|  |  | +},{}],107:[function(require,module,exports){
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Apply this component to models that looks "blocky", to have Three.js compute
 | 
	
		
			
				|  |  | + * vertex normals on the fly for a "smoother" look.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +module.exports = {
 | 
	
		
			
				|  |  | +  init: function () {
 | 
	
		
			
				|  |  | +    this.el.addEventListener('model-loaded', function (e) {
 | 
	
		
			
				|  |  | +      e.detail.model.traverse(function (node) {
 | 
	
		
			
				|  |  | +        if (node.isMesh) node.geometry.computeVertexNormals();
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  | +    })
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		var polygons = zoneNodes[zone].groups[group];
 | 
	
		
			
				|  |  | +},{}],108:[function(require,module,exports){
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Based on aframe/examples/showcase/tracked-controls.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Implement bounding sphere collision detection for entities with a mesh.
 | 
	
		
			
				|  |  | + * Sets the specified state on the intersected entities.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * @property {string} objects - Selector of the entities to test for collision.
 | 
	
		
			
				|  |  | + * @property {string} state - State to set on collided entities.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +module.exports = {
 | 
	
		
			
				|  |  | +  schema: {
 | 
	
		
			
				|  |  | +    objects: {default: ''},
 | 
	
		
			
				|  |  | +    state: {default: 'collided'},
 | 
	
		
			
				|  |  | +    radius: {default: 0.05},
 | 
	
		
			
				|  |  | +    watch: {default: true}
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		polygons.forEach((p) => {
 | 
	
		
			
				|  |  | -			if (nearPosition && nearRange) {
 | 
	
		
			
				|  |  | -				if (utils.distanceToSquared(nearPosition, p.centroid) < nearRange * nearRange) {
 | 
	
		
			
				|  |  | -					candidates.push(p.centroid);
 | 
	
		
			
				|  |  | -				}
 | 
	
		
			
				|  |  | -			} else {
 | 
	
		
			
				|  |  | -				candidates.push(p.centroid);
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -		});
 | 
	
		
			
				|  |  | +  init: function () {
 | 
	
		
			
				|  |  | +    /** @type {MutationObserver} */
 | 
	
		
			
				|  |  | +    this.observer = null;
 | 
	
		
			
				|  |  | +    /** @type {Array<Element>} Elements to watch for collisions. */
 | 
	
		
			
				|  |  | +    this.els = [];
 | 
	
		
			
				|  |  | +    /** @type {Array<Element>} Elements currently in collision state. */
 | 
	
		
			
				|  |  | +    this.collisions = [];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		return utils.sample(candidates) || new THREE.Vector3();
 | 
	
		
			
				|  |  | -	},
 | 
	
		
			
				|  |  | -	getClosestNode: function (position, zone, group, checkPolygon = false) {
 | 
	
		
			
				|  |  | -		const nodes = zoneNodes[zone].groups[group];
 | 
	
		
			
				|  |  | -		const vertices = zoneNodes[zone].vertices;
 | 
	
		
			
				|  |  | -		let closestNode = null;
 | 
	
		
			
				|  |  | -		let closestDistance = Infinity;
 | 
	
		
			
				|  |  | +    this.handleHit = this.handleHit.bind(this);
 | 
	
		
			
				|  |  | +    this.handleHitEnd = this.handleHitEnd.bind(this);
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		nodes.forEach((node) => {
 | 
	
		
			
				|  |  | -			const distance = utils.distanceToSquared(node.centroid, position);
 | 
	
		
			
				|  |  | -			if (distance < closestDistance
 | 
	
		
			
				|  |  | -					&& (!checkPolygon || utils.isVectorInPolygon(position, node, vertices))) {
 | 
	
		
			
				|  |  | -				closestNode = node;
 | 
	
		
			
				|  |  | -				closestDistance = distance;
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -		});
 | 
	
		
			
				|  |  | +  remove: function () {
 | 
	
		
			
				|  |  | +    this.pause();
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		return closestNode;
 | 
	
		
			
				|  |  | -	},
 | 
	
		
			
				|  |  | -	findPath: function (startPosition, targetPosition, zone, group) {
 | 
	
		
			
				|  |  | -		const nodes = zoneNodes[zone].groups[group];
 | 
	
		
			
				|  |  | -		const vertices = zoneNodes[zone].vertices;
 | 
	
		
			
				|  |  | +  play: function () {
 | 
	
		
			
				|  |  | +    var sceneEl = this.el.sceneEl;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		const closestNode = this.getClosestNode(startPosition, zone, group);
 | 
	
		
			
				|  |  | -		const farthestNode = this.getClosestNode(targetPosition, zone, group, true);
 | 
	
		
			
				|  |  | +    if (this.data.watch) {
 | 
	
		
			
				|  |  | +      this.observer = new MutationObserver(this.update.bind(this, null));
 | 
	
		
			
				|  |  | +      this.observer.observe(sceneEl, {childList: true, subtree: true});
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		// If we can't find any node, just go straight to the target
 | 
	
		
			
				|  |  | -		if (!closestNode || !farthestNode) {
 | 
	
		
			
				|  |  | -			return null;
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | +  pause: function () {
 | 
	
		
			
				|  |  | +    if (this.observer) {
 | 
	
		
			
				|  |  | +      this.observer.disconnect();
 | 
	
		
			
				|  |  | +      this.observer = null;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		const paths = AStar.search(nodes, closestNode, farthestNode);
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * Update list of entities to test for collision.
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  update: function () {
 | 
	
		
			
				|  |  | +    var data = this.data;
 | 
	
		
			
				|  |  | +    var objectEls;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		const getPortalFromTo = function (a, b) {
 | 
	
		
			
				|  |  | -			for (var i = 0; i < a.neighbours.length; i++) {
 | 
	
		
			
				|  |  | -				if (a.neighbours[i] === b.id) {
 | 
	
		
			
				|  |  | -					return a.portals[i];
 | 
	
		
			
				|  |  | -				}
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -		};
 | 
	
		
			
				|  |  | +    // Push entities into list of els to intersect.
 | 
	
		
			
				|  |  | +    if (data.objects) {
 | 
	
		
			
				|  |  | +      objectEls = this.el.sceneEl.querySelectorAll(data.objects);
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +      // If objects not defined, intersect with everything.
 | 
	
		
			
				|  |  | +      objectEls = this.el.sceneEl.children;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    // Convert from NodeList to Array
 | 
	
		
			
				|  |  | +    this.els = Array.prototype.slice.call(objectEls);
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		// We have the corridor, now pull the rope.
 | 
	
		
			
				|  |  | -		const channel = new Channel();
 | 
	
		
			
				|  |  | -		channel.push(startPosition);
 | 
	
		
			
				|  |  | -		for (let i = 0; i < paths.length; i++) {
 | 
	
		
			
				|  |  | -			const polygon = paths[i];
 | 
	
		
			
				|  |  | -			const nextPolygon = paths[i + 1];
 | 
	
		
			
				|  |  | +  tick: (function () {
 | 
	
		
			
				|  |  | +    var position = new THREE.Vector3(),
 | 
	
		
			
				|  |  | +        meshPosition = new THREE.Vector3(),
 | 
	
		
			
				|  |  | +        meshScale = new THREE.Vector3(),
 | 
	
		
			
				|  |  | +        colliderScale = new THREE.Vector3(),
 | 
	
		
			
				|  |  | +        distanceMap = new Map();
 | 
	
		
			
				|  |  | +    return function () {
 | 
	
		
			
				|  |  | +      var el = this.el,
 | 
	
		
			
				|  |  | +          data = this.data,
 | 
	
		
			
				|  |  | +          mesh = el.getObject3D('mesh'),
 | 
	
		
			
				|  |  | +          colliderRadius,
 | 
	
		
			
				|  |  | +          collisions = [];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -			if (nextPolygon) {
 | 
	
		
			
				|  |  | -				const portals = getPortalFromTo(polygon, nextPolygon);
 | 
	
		
			
				|  |  | -				channel.push(
 | 
	
		
			
				|  |  | -					vertices[portals[0]],
 | 
	
		
			
				|  |  | -					vertices[portals[1]]
 | 
	
		
			
				|  |  | -				);
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | -		channel.push(targetPosition);
 | 
	
		
			
				|  |  | -		channel.stringPull();
 | 
	
		
			
				|  |  | +      if (!mesh) { return; }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		// Return the path, omitting first position (which is already known).
 | 
	
		
			
				|  |  | -		const path = channel.path.map((c) => new THREE.Vector3(c.x, c.y, c.z));
 | 
	
		
			
				|  |  | -		path.shift();
 | 
	
		
			
				|  |  | -		return path;
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | +      distanceMap.clear();
 | 
	
		
			
				|  |  | +      position.copy(el.object3D.getWorldPosition());
 | 
	
		
			
				|  |  | +      el.object3D.getWorldScale(colliderScale);
 | 
	
		
			
				|  |  | +      colliderRadius = data.radius * scaleFactor(colliderScale);
 | 
	
		
			
				|  |  | +      // Update collision list.
 | 
	
		
			
				|  |  | +      this.els.forEach(intersect);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{"./AStar":116,"./Channel":118,"./utils":120}],120:[function(require,module,exports){
 | 
	
		
			
				|  |  | -class Utils {
 | 
	
		
			
				|  |  | +      // Emit events and add collision states, in order of distance.
 | 
	
		
			
				|  |  | +      collisions
 | 
	
		
			
				|  |  | +        .sort(function (a, b) {
 | 
	
		
			
				|  |  | +          return distanceMap.get(a) > distanceMap.get(b) ? 1 : -1;
 | 
	
		
			
				|  |  | +        })
 | 
	
		
			
				|  |  | +        .forEach(this.handleHit);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  static computeCentroids (geometry) {
 | 
	
		
			
				|  |  | -    var f, fl, face;
 | 
	
		
			
				|  |  | +      // Remove collision state from current element.
 | 
	
		
			
				|  |  | +      if (collisions.length === 0) { el.emit('hit', {el: null}); }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    for ( f = 0, fl = geometry.faces.length; f < fl; f ++ ) {
 | 
	
		
			
				|  |  | +      // Remove collision state from other elements.
 | 
	
		
			
				|  |  | +      this.collisions.filter(function (el) {
 | 
	
		
			
				|  |  | +        return !distanceMap.has(el);
 | 
	
		
			
				|  |  | +      }).forEach(this.handleHitEnd);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      face = geometry.faces[ f ];
 | 
	
		
			
				|  |  | -      face.centroid = new THREE.Vector3( 0, 0, 0 );
 | 
	
		
			
				|  |  | +      // Store new collisions
 | 
	
		
			
				|  |  | +      this.collisions = collisions;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      face.centroid.add( geometry.vertices[ face.a ] );
 | 
	
		
			
				|  |  | -      face.centroid.add( geometry.vertices[ face.b ] );
 | 
	
		
			
				|  |  | -      face.centroid.add( geometry.vertices[ face.c ] );
 | 
	
		
			
				|  |  | -      face.centroid.divideScalar( 3 );
 | 
	
		
			
				|  |  | +      // Bounding sphere collision detection
 | 
	
		
			
				|  |  | +      function intersect (el) {
 | 
	
		
			
				|  |  | +        var radius, mesh, distance, box, extent, size;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +        if (!el.isEntity) { return; }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  static roundNumber (number, decimals) {
 | 
	
		
			
				|  |  | -    var newnumber = Number(number + '').toFixed(parseInt(decimals));
 | 
	
		
			
				|  |  | -    return parseFloat(newnumber);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +        mesh = el.getObject3D('mesh');
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  static sample (list) {
 | 
	
		
			
				|  |  | -    return list[Math.floor(Math.random() * list.length)];
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +        if (!mesh) { return; }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  static mergeVertexIds (aList, bList) {
 | 
	
		
			
				|  |  | +        box = new THREE.Box3().setFromObject(mesh);
 | 
	
		
			
				|  |  | +        size = box.getSize();
 | 
	
		
			
				|  |  | +        extent = Math.max(size.x, size.y, size.z) / 2;
 | 
	
		
			
				|  |  | +        radius = Math.sqrt(2 * extent * extent);
 | 
	
		
			
				|  |  | +        box.getCenter(meshPosition);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    var sharedVertices = [];
 | 
	
		
			
				|  |  | +        if (!radius) { return; }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    aList.forEach((vID) => {
 | 
	
		
			
				|  |  | -      if (bList.indexOf(vID) >= 0) {
 | 
	
		
			
				|  |  | -        sharedVertices.push(vID);
 | 
	
		
			
				|  |  | +        distance = position.distanceTo(meshPosition);
 | 
	
		
			
				|  |  | +        if (distance < radius + colliderRadius) {
 | 
	
		
			
				|  |  | +          collisions.push(el);
 | 
	
		
			
				|  |  | +          distanceMap.set(el, distance);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | -    });
 | 
	
		
			
				|  |  | +      // use max of scale factors to maintain bounding sphere collision
 | 
	
		
			
				|  |  | +      function scaleFactor (scaleVec) {
 | 
	
		
			
				|  |  | +        return Math.max.apply(null, scaleVec.toArray());
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +  })(),
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (sharedVertices.length < 2) return [];
 | 
	
		
			
				|  |  | +  handleHit: function (targetEl) {
 | 
	
		
			
				|  |  | +    targetEl.emit('hit');
 | 
	
		
			
				|  |  | +    targetEl.addState(this.data.state);
 | 
	
		
			
				|  |  | +    this.el.emit('hit', {el: targetEl});
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  | +  handleHitEnd: function (targetEl) {
 | 
	
		
			
				|  |  | +    targetEl.emit('hitend');
 | 
	
		
			
				|  |  | +    targetEl.removeState(this.data.state);
 | 
	
		
			
				|  |  | +    this.el.emit('hitend', {el: targetEl});
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (sharedVertices.includes(aList[0]) && sharedVertices.includes(aList[aList.length - 1])) {
 | 
	
		
			
				|  |  | -      // Vertices on both edges are bad, so shift them once to the left
 | 
	
		
			
				|  |  | -      aList.push(aList.shift());
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +},{}],109:[function(require,module,exports){
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Toggle velocity.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Moves an object back and forth along an axis, within a min/max extent.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +module.exports = {
 | 
	
		
			
				|  |  | +  dependencies: ['velocity'],
 | 
	
		
			
				|  |  | +  schema: {
 | 
	
		
			
				|  |  | +    axis: { default: 'x', oneOf: ['x', 'y', 'z'] },
 | 
	
		
			
				|  |  | +    min: { default: 0 },
 | 
	
		
			
				|  |  | +    max: { default: 0 },
 | 
	
		
			
				|  |  | +    speed: { default: 1 }
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  | +  init: function () {
 | 
	
		
			
				|  |  | +    var velocity = {x: 0, y: 0, z: 0};
 | 
	
		
			
				|  |  | +    velocity[this.data.axis] = this.data.speed;
 | 
	
		
			
				|  |  | +    this.el.setAttribute('velocity', velocity);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (sharedVertices.includes(bList[0]) && sharedVertices.includes(bList[bList.length - 1])) {
 | 
	
		
			
				|  |  | -      // Vertices on both edges are bad, so shift them once to the left
 | 
	
		
			
				|  |  | -      bList.push(bList.shift());
 | 
	
		
			
				|  |  | +    if (this.el.sceneEl.addBehavior) this.el.sceneEl.addBehavior(this);
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  | +  remove: function () {},
 | 
	
		
			
				|  |  | +  update: function () { this.tick(); },
 | 
	
		
			
				|  |  | +  tick: function () {
 | 
	
		
			
				|  |  | +    var data = this.data,
 | 
	
		
			
				|  |  | +        velocity = this.el.getAttribute('velocity'),
 | 
	
		
			
				|  |  | +        position = this.el.getAttribute('position');
 | 
	
		
			
				|  |  | +    if (velocity[data.axis] > 0 && position[data.axis] > data.max) {
 | 
	
		
			
				|  |  | +      velocity[data.axis] = -data.speed;
 | 
	
		
			
				|  |  | +      this.el.setAttribute('velocity', velocity);
 | 
	
		
			
				|  |  | +    } else if (velocity[data.axis] < 0 && position[data.axis] < data.min) {
 | 
	
		
			
				|  |  | +      velocity[data.axis] = data.speed;
 | 
	
		
			
				|  |  | +      this.el.setAttribute('velocity', velocity);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // Again!
 | 
	
		
			
				|  |  | -    sharedVertices = [];
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    aList.forEach((vId) => {
 | 
	
		
			
				|  |  | -      if (bList.includes(vId)) {
 | 
	
		
			
				|  |  | -        sharedVertices.push(vId);
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -    });
 | 
	
		
			
				|  |  | +},{}],110:[function(require,module,exports){
 | 
	
		
			
				|  |  | +module.exports = {
 | 
	
		
			
				|  |  | +  'nav-mesh':    require('./nav-mesh'),
 | 
	
		
			
				|  |  | +  'nav-controller':     require('./nav-controller'),
 | 
	
		
			
				|  |  | +  'system':      require('./system'),
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    var clockwiseMostSharedVertex = sharedVertices[1];
 | 
	
		
			
				|  |  | -    var counterClockwiseMostSharedVertex = sharedVertices[0];
 | 
	
		
			
				|  |  | +  registerAll: function (AFRAME) {
 | 
	
		
			
				|  |  | +    if (this._registered) return;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    AFRAME = AFRAME || window.AFRAME;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    var cList = aList.slice();
 | 
	
		
			
				|  |  | -    while (cList[0] !== clockwiseMostSharedVertex) {
 | 
	
		
			
				|  |  | -      cList.push(cList.shift());
 | 
	
		
			
				|  |  | +    if (!AFRAME.components['nav-mesh']) {
 | 
	
		
			
				|  |  | +      AFRAME.registerComponent('nav-mesh', this['nav-mesh']);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    var c = 0;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    var temp = bList.slice();
 | 
	
		
			
				|  |  | -    while (temp[0] !== counterClockwiseMostSharedVertex) {
 | 
	
		
			
				|  |  | -      temp.push(temp.shift());
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      if (c++ > 10) throw new Error('Unexpected state');
 | 
	
		
			
				|  |  | +    if (!AFRAME.components['nav-controller']) {
 | 
	
		
			
				|  |  | +      AFRAME.registerComponent('nav-controller',  this['nav-controller']);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // Shave
 | 
	
		
			
				|  |  | -    temp.shift();
 | 
	
		
			
				|  |  | -    temp.pop();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    cList = cList.concat(temp);
 | 
	
		
			
				|  |  | +    if (!AFRAME.systems.nav) {
 | 
	
		
			
				|  |  | +      AFRAME.registerSystem('nav', this.system);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    return cList;
 | 
	
		
			
				|  |  | +    this._registered = true;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  static setPolygonCentroid (polygon, navigationMesh) {
 | 
	
		
			
				|  |  | -    var sum = new THREE.Vector3();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    var vertices = navigationMesh.vertices;
 | 
	
		
			
				|  |  | +},{"./nav-controller":111,"./nav-mesh":112,"./system":113}],111:[function(require,module,exports){
 | 
	
		
			
				|  |  | +module.exports = {
 | 
	
		
			
				|  |  | +  schema: {
 | 
	
		
			
				|  |  | +    destination: {type: 'vec3'},
 | 
	
		
			
				|  |  | +    active: {default: false},
 | 
	
		
			
				|  |  | +    speed: {default: 2}
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  | +  init: function () {
 | 
	
		
			
				|  |  | +    this.system = this.el.sceneEl.systems.nav;
 | 
	
		
			
				|  |  | +    this.system.addController(this);
 | 
	
		
			
				|  |  | +    this.path = [];
 | 
	
		
			
				|  |  | +    this.raycaster = new THREE.Raycaster();
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  | +  remove: function () {
 | 
	
		
			
				|  |  | +    this.system.removeController(this);
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  | +  update: function () {
 | 
	
		
			
				|  |  | +    this.path.length = 0;
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  | +  tick: (function () {
 | 
	
		
			
				|  |  | +    var vDest = new THREE.Vector3();
 | 
	
		
			
				|  |  | +    var vDelta = new THREE.Vector3();
 | 
	
		
			
				|  |  | +    var vNext = new THREE.Vector3();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    polygon.vertexIds.forEach((vId) => {
 | 
	
		
			
				|  |  | -      sum.add(vertices[vId]);
 | 
	
		
			
				|  |  | -    });
 | 
	
		
			
				|  |  | +    return function (t, dt) {
 | 
	
		
			
				|  |  | +      var el = this.el;
 | 
	
		
			
				|  |  | +      var data = this.data;
 | 
	
		
			
				|  |  | +      var raycaster = this.raycaster;
 | 
	
		
			
				|  |  | +      var speed = data.speed * dt / 1000;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    sum.divideScalar(polygon.vertexIds.length);
 | 
	
		
			
				|  |  | +      if (!data.active) return;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    polygon.centroid.copy(sum);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +      // Use PatrolJS pathfinding system to get shortest path to target.
 | 
	
		
			
				|  |  | +      if (!this.path.length) {
 | 
	
		
			
				|  |  | +        this.path = this.system.getPath(this.el.object3D, vDest.copy(data.destination));
 | 
	
		
			
				|  |  | +        this.path = this.path || [];
 | 
	
		
			
				|  |  | +        el.emit('nav-start');
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  static cleanPolygon (polygon, navigationMesh) {
 | 
	
		
			
				|  |  | +      // If no path is found, exit.
 | 
	
		
			
				|  |  | +      if (!this.path.length) {
 | 
	
		
			
				|  |  | +        console.warn('[nav] Unable to find path to %o.', data.destination);
 | 
	
		
			
				|  |  | +        this.el.setAttribute('nav-controller', {active: false});
 | 
	
		
			
				|  |  | +        el.emit('nav-end');
 | 
	
		
			
				|  |  | +        return;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    var newVertexIds = [];
 | 
	
		
			
				|  |  | +      // Current segment is a vector from current position to next waypoint.
 | 
	
		
			
				|  |  | +      var vCurrent = el.object3D.position;
 | 
	
		
			
				|  |  | +      var vWaypoint = this.path[0];
 | 
	
		
			
				|  |  | +      vDelta.subVectors(vWaypoint, vCurrent);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    var vertices = navigationMesh.vertices;
 | 
	
		
			
				|  |  | +      var distance = vDelta.length();
 | 
	
		
			
				|  |  | +      var gazeTarget;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    for (var i = 0; i < polygon.vertexIds.length; i++) {
 | 
	
		
			
				|  |  | +      if (distance < speed) {
 | 
	
		
			
				|  |  | +        // If <1 step from current waypoint, discard it and move toward next.
 | 
	
		
			
				|  |  | +        this.path.shift();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      var vertex = vertices[polygon.vertexIds[i]];
 | 
	
		
			
				|  |  | +        // After discarding the last waypoint, exit pathfinding.
 | 
	
		
			
				|  |  | +        if (!this.path.length) {
 | 
	
		
			
				|  |  | +          this.el.setAttribute('nav-controller', {active: false});
 | 
	
		
			
				|  |  | +          el.emit('nav-end');
 | 
	
		
			
				|  |  | +          return;
 | 
	
		
			
				|  |  | +        } else {
 | 
	
		
			
				|  |  | +          gazeTarget = this.path[0];
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        // If still far away from next waypoint, find next position for
 | 
	
		
			
				|  |  | +        // the current frame.
 | 
	
		
			
				|  |  | +        vNext.copy(vDelta.setLength(speed)).add(vCurrent);
 | 
	
		
			
				|  |  | +        gazeTarget = vWaypoint;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      var nextVertexId, previousVertexId;
 | 
	
		
			
				|  |  | -      var nextVertex, previousVertex;
 | 
	
		
			
				|  |  | +      // Look at the next waypoint.
 | 
	
		
			
				|  |  | +      gazeTarget.y = vCurrent.y;
 | 
	
		
			
				|  |  | +      el.object3D.lookAt(gazeTarget);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      // console.log("nextVertex: ", nextVertex);
 | 
	
		
			
				|  |  | +      // Raycast against the nav mesh, to keep the controller moving along the
 | 
	
		
			
				|  |  | +      // ground, not traveling in a straight line from higher to lower waypoints.
 | 
	
		
			
				|  |  | +      raycaster.ray.origin.copy(vNext);
 | 
	
		
			
				|  |  | +      raycaster.ray.origin.y += 1.5;
 | 
	
		
			
				|  |  | +      raycaster.ray.direction.y = -1;
 | 
	
		
			
				|  |  | +      var intersections = raycaster.intersectObject(this.system.getNavMesh());
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      if (i === 0) {
 | 
	
		
			
				|  |  | -        nextVertexId = polygon.vertexIds[1];
 | 
	
		
			
				|  |  | -        previousVertexId = polygon.vertexIds[polygon.vertexIds.length - 1];
 | 
	
		
			
				|  |  | -      } else if (i === polygon.vertexIds.length - 1) {
 | 
	
		
			
				|  |  | -        nextVertexId = polygon.vertexIds[0];
 | 
	
		
			
				|  |  | -        previousVertexId = polygon.vertexIds[polygon.vertexIds.length - 2];
 | 
	
		
			
				|  |  | +      if (!intersections.length) {
 | 
	
		
			
				|  |  | +        // Raycasting failed. Step toward the waypoint and hope for the best.
 | 
	
		
			
				|  |  | +        vCurrent.copy(vNext);
 | 
	
		
			
				|  |  |        } else {
 | 
	
		
			
				|  |  | -        nextVertexId = polygon.vertexIds[i + 1];
 | 
	
		
			
				|  |  | -        previousVertexId = polygon.vertexIds[i - 1];
 | 
	
		
			
				|  |  | +        // Re-project next position onto nav mesh.
 | 
	
		
			
				|  |  | +        vDelta.subVectors(intersections[0].point, vCurrent);
 | 
	
		
			
				|  |  | +        vCurrent.add(vDelta.setLength(speed));
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      nextVertex = vertices[nextVertexId];
 | 
	
		
			
				|  |  | -      previousVertex = vertices[previousVertexId];
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +  }())
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      var a = nextVertex.clone().sub(vertex);
 | 
	
		
			
				|  |  | -      var b = previousVertex.clone().sub(vertex);
 | 
	
		
			
				|  |  | +},{}],112:[function(require,module,exports){
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * nav-mesh
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Waits for a mesh to be loaded on the current entity, then sets it as the
 | 
	
		
			
				|  |  | + * nav mesh in the pathfinding system.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +module.exports = {
 | 
	
		
			
				|  |  | +  init: function () {
 | 
	
		
			
				|  |  | +    this.system = this.el.sceneEl.systems.nav;
 | 
	
		
			
				|  |  | +    this.loadNavMesh();
 | 
	
		
			
				|  |  | +    this.el.addEventListener('model-loaded', this.loadNavMesh.bind(this));
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      var angle = a.angleTo(b);
 | 
	
		
			
				|  |  | +  loadNavMesh: function () {
 | 
	
		
			
				|  |  | +    var object = this.el.getObject3D('mesh');
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      // console.log(angle);
 | 
	
		
			
				|  |  | +    if (!object) return;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      if (angle > Math.PI - 0.01 && angle < Math.PI + 0.01) {
 | 
	
		
			
				|  |  | -        // Unneccesary vertex
 | 
	
		
			
				|  |  | -        // console.log("Unneccesary vertex: ", polygon.vertexIds[i]);
 | 
	
		
			
				|  |  | -        // console.log("Angle between "+previousVertexId+", "+polygon.vertexIds[i]+" "+nextVertexId+" was: ", angle);
 | 
	
		
			
				|  |  | +    var navMesh;
 | 
	
		
			
				|  |  | +    object.traverse(function (node) {
 | 
	
		
			
				|  |  | +      if (node.isMesh) navMesh = node;
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    if (!navMesh) return;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        // Remove the neighbours who had this vertex
 | 
	
		
			
				|  |  | -        var goodNeighbours = [];
 | 
	
		
			
				|  |  | -        polygon.neighbours.forEach((neighbour) => {
 | 
	
		
			
				|  |  | -          if (!neighbour.vertexIds.includes(polygon.vertexIds[i])) {
 | 
	
		
			
				|  |  | -            goodNeighbours.push(neighbour);
 | 
	
		
			
				|  |  | -          }
 | 
	
		
			
				|  |  | -        });
 | 
	
		
			
				|  |  | -        polygon.neighbours = goodNeighbours;
 | 
	
		
			
				|  |  | +    this.system.setNavMesh(navMesh);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +},{}],113:[function(require,module,exports){
 | 
	
		
			
				|  |  | +var Path = require('three-pathfinding');
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        // TODO cleanup the list of vertices and rebuild vertexIds for all polygons
 | 
	
		
			
				|  |  | -      } else {
 | 
	
		
			
				|  |  | -        newVertexIds.push(polygon.vertexIds[i]);
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * nav
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Pathfinding system, using PatrolJS.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +module.exports = {
 | 
	
		
			
				|  |  | +  init: function () {
 | 
	
		
			
				|  |  | +    this.navMesh = null;
 | 
	
		
			
				|  |  | +    this.nodes = null;
 | 
	
		
			
				|  |  | +    this.controllers = new Set();
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * @param {THREE.Mesh} mesh
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  setNavMesh: function (mesh) {
 | 
	
		
			
				|  |  | +    var geometry = mesh.geometry.isBufferGeometry
 | 
	
		
			
				|  |  | +      ? new THREE.Geometry().fromBufferGeometry(mesh.geometry)
 | 
	
		
			
				|  |  | +      : mesh.geometry;
 | 
	
		
			
				|  |  | +    this.navMesh = new THREE.Mesh(geometry);
 | 
	
		
			
				|  |  | +    this.nodes = Path.buildNodes(this.navMesh.geometry);
 | 
	
		
			
				|  |  | +    Path.setZoneData('level', this.nodes);
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // console.log("New vertexIds: ", newVertexIds);
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * @return {THREE.Mesh}
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  getNavMesh: function () {
 | 
	
		
			
				|  |  | +    return this.navMesh;
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    polygon.vertexIds = newVertexIds;
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * @param {NavController} ctrl
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  addController: function (ctrl) {
 | 
	
		
			
				|  |  | +    this.controllers.add(ctrl);
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    setPolygonCentroid(polygon, navigationMesh);
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * @param {NavController} ctrl
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  removeController: function (ctrl) {
 | 
	
		
			
				|  |  | +    this.controllers.remove(ctrl);
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * @param  {NavController} ctrl
 | 
	
		
			
				|  |  | +   * @param  {THREE.Vector3} target
 | 
	
		
			
				|  |  | +   * @return {Array<THREE.Vector3>}
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  getPath: function (ctrl, target) {
 | 
	
		
			
				|  |  | +    var start = ctrl.el.object3D.position;
 | 
	
		
			
				|  |  | +    // TODO(donmccurdy): Current group should be cached.
 | 
	
		
			
				|  |  | +    var group = Path.getGroup('level', start);
 | 
	
		
			
				|  |  | +    return Path.findPath(start, target, 'level', group);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  static isConvex (polygon, navigationMesh) {
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    var vertices = navigationMesh.vertices;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    if (polygon.vertexIds.length < 3) return false;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    var convex = true;
 | 
	
		
			
				|  |  | +},{"three-pathfinding":82}],114:[function(require,module,exports){
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Flat grid.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Defaults to 75x75.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +var Primitive = module.exports = {
 | 
	
		
			
				|  |  | +  defaultComponents: {
 | 
	
		
			
				|  |  | +    geometry: {
 | 
	
		
			
				|  |  | +      primitive: 'plane',
 | 
	
		
			
				|  |  | +      width: 75,
 | 
	
		
			
				|  |  | +      height: 75
 | 
	
		
			
				|  |  | +    },
 | 
	
		
			
				|  |  | +    rotation: {x: -90, y: 0, z: 0},
 | 
	
		
			
				|  |  | +    material: {
 | 
	
		
			
				|  |  | +      src: 'url(https://cdn.rawgit.com/donmccurdy/aframe-extras/v1.16.3/assets/grid.png)',
 | 
	
		
			
				|  |  | +      repeat: '75 75'
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  | +  mappings: {
 | 
	
		
			
				|  |  | +    width: 'geometry.width',
 | 
	
		
			
				|  |  | +    height: 'geometry.height',
 | 
	
		
			
				|  |  | +    src: 'material.src'
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    var total = 0;
 | 
	
		
			
				|  |  | +module.exports.registerAll = (function () {
 | 
	
		
			
				|  |  | +  var registered = false;
 | 
	
		
			
				|  |  | +  return function (AFRAME) {
 | 
	
		
			
				|  |  | +    if (registered) return;
 | 
	
		
			
				|  |  | +    AFRAME = AFRAME || window.AFRAME;
 | 
	
		
			
				|  |  | +    AFRAME.registerPrimitive('a-grid', Primitive);
 | 
	
		
			
				|  |  | +    registered = true;
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +}());
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    var results = [];
 | 
	
		
			
				|  |  | +},{}],115:[function(require,module,exports){
 | 
	
		
			
				|  |  | +var vg = require('../../lib/hex-grid.min.js');
 | 
	
		
			
				|  |  | +var defaultHexGrid = require('../../lib/default-hex-grid.json');
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    for (var i = 0; i < polygon.vertexIds.length; i++) {
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Hex grid.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +var Primitive = module.exports.Primitive = {
 | 
	
		
			
				|  |  | +  defaultComponents: {
 | 
	
		
			
				|  |  | +    'hexgrid': {}
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  | +  mappings: {
 | 
	
		
			
				|  |  | +    src: 'hexgrid.src'
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      var vertex = vertices[polygon.vertexIds[i]];
 | 
	
		
			
				|  |  | +var Component = module.exports.Component = {
 | 
	
		
			
				|  |  | +  dependencies: ['material'],
 | 
	
		
			
				|  |  | +  schema: {
 | 
	
		
			
				|  |  | +    src: {type: 'asset'}
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  | +  init: function () {
 | 
	
		
			
				|  |  | +    var data = this.data;
 | 
	
		
			
				|  |  | +    if (data.src) {
 | 
	
		
			
				|  |  | +      fetch(data.src)
 | 
	
		
			
				|  |  | +        .then(function (response) { response.json(); })
 | 
	
		
			
				|  |  | +        .then(function (json) { this.addMesh(json); });
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +      this.addMesh(defaultHexGrid);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  | +  addMesh: function (json) {
 | 
	
		
			
				|  |  | +    var grid = new vg.HexGrid();
 | 
	
		
			
				|  |  | +    grid.fromJSON(json);
 | 
	
		
			
				|  |  | +    var board = new vg.Board(grid);
 | 
	
		
			
				|  |  | +    board.generateTilemap();
 | 
	
		
			
				|  |  | +    this.el.setObject3D('mesh', board.group);
 | 
	
		
			
				|  |  | +    this.addMaterial();
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  | +  addMaterial: function () {
 | 
	
		
			
				|  |  | +    var materialComponent = this.el.components.material;
 | 
	
		
			
				|  |  | +    var material = (materialComponent || {}).material;
 | 
	
		
			
				|  |  | +    if (!material) return;
 | 
	
		
			
				|  |  | +    this.el.object3D.traverse(function (node) {
 | 
	
		
			
				|  |  | +      if (node.isMesh) {
 | 
	
		
			
				|  |  | +        node.material = material;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  | +  remove: function () {
 | 
	
		
			
				|  |  | +    this.el.removeObject3D('mesh');
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      var nextVertex, previousVertex;
 | 
	
		
			
				|  |  | +module.exports.registerAll = (function () {
 | 
	
		
			
				|  |  | +  var registered = false;
 | 
	
		
			
				|  |  | +  return function (AFRAME) {
 | 
	
		
			
				|  |  | +    if (registered) return;
 | 
	
		
			
				|  |  | +    AFRAME = AFRAME || window.AFRAME;
 | 
	
		
			
				|  |  | +    AFRAME.registerComponent('hexgrid', Component);
 | 
	
		
			
				|  |  | +    AFRAME.registerPrimitive('a-hexgrid', Primitive);
 | 
	
		
			
				|  |  | +    registered = true;
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +}());
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      if (i === 0) {
 | 
	
		
			
				|  |  | -        nextVertex = vertices[polygon.vertexIds[1]];
 | 
	
		
			
				|  |  | -        previousVertex = vertices[polygon.vertexIds[polygon.vertexIds.length - 1]];
 | 
	
		
			
				|  |  | -      } else if (i === polygon.vertexIds.length - 1) {
 | 
	
		
			
				|  |  | -        nextVertex = vertices[polygon.vertexIds[0]];
 | 
	
		
			
				|  |  | -        previousVertex = vertices[polygon.vertexIds[polygon.vertexIds.length - 2]];
 | 
	
		
			
				|  |  | -      } else {
 | 
	
		
			
				|  |  | -        nextVertex = vertices[polygon.vertexIds[i + 1]];
 | 
	
		
			
				|  |  | -        previousVertex = vertices[polygon.vertexIds[i - 1]];
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | +},{"../../lib/default-hex-grid.json":7,"../../lib/hex-grid.min.js":9}],116:[function(require,module,exports){
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Flat-shaded ocean primitive.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Based on a Codrops tutorial:
 | 
	
		
			
				|  |  | + * http://tympanus.net/codrops/2016/04/26/the-aviator-animating-basic-3d-scene-threejs/
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +var Primitive = module.exports.Primitive = {
 | 
	
		
			
				|  |  | +  defaultComponents: {
 | 
	
		
			
				|  |  | +    ocean: {},
 | 
	
		
			
				|  |  | +    rotation: {x: -90, y: 0, z: 0}
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  | +  mappings: {
 | 
	
		
			
				|  |  | +    width: 'ocean.width',
 | 
	
		
			
				|  |  | +    depth: 'ocean.depth',
 | 
	
		
			
				|  |  | +    density: 'ocean.density',
 | 
	
		
			
				|  |  | +    color: 'ocean.color',
 | 
	
		
			
				|  |  | +    opacity: 'ocean.opacity'
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      var a = nextVertex.clone().sub(vertex);
 | 
	
		
			
				|  |  | -      var b = previousVertex.clone().sub(vertex);
 | 
	
		
			
				|  |  | +var Component = module.exports.Component = {
 | 
	
		
			
				|  |  | +  schema: {
 | 
	
		
			
				|  |  | +    // Dimensions of the ocean area.
 | 
	
		
			
				|  |  | +    width: {default: 10, min: 0},
 | 
	
		
			
				|  |  | +    depth: {default: 10, min: 0},
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      var angle = a.angleTo(b);
 | 
	
		
			
				|  |  | -      total += angle;
 | 
	
		
			
				|  |  | +    // Density of waves.
 | 
	
		
			
				|  |  | +    density: {default: 10},
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      if (angle === Math.PI || angle === 0) return false;
 | 
	
		
			
				|  |  | +    // Wave amplitude and variance.
 | 
	
		
			
				|  |  | +    amplitude: {default: 0.1},
 | 
	
		
			
				|  |  | +    amplitudeVariance: {default: 0.3},
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      var r = a.cross(b).y;
 | 
	
		
			
				|  |  | -      results.push(r);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +    // Wave speed and variance.
 | 
	
		
			
				|  |  | +    speed: {default: 1},
 | 
	
		
			
				|  |  | +    speedVariance: {default: 2},
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // if ( total > (polygon.vertexIds.length-2)*Math.PI ) return false;
 | 
	
		
			
				|  |  | +    // Material.
 | 
	
		
			
				|  |  | +    color: {default: '#7AD2F7', type: 'color'},
 | 
	
		
			
				|  |  | +    opacity: {default: 0.8}
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    results.forEach((r) => {
 | 
	
		
			
				|  |  | -      if (r === 0) convex = false;
 | 
	
		
			
				|  |  | -    });
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * Use play() instead of init(), because component mappings – unavailable as dependencies – are
 | 
	
		
			
				|  |  | +   * not guaranteed to have parsed when this component is initialized.
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  play: function () {
 | 
	
		
			
				|  |  | +    var el = this.el,
 | 
	
		
			
				|  |  | +        data = this.data,
 | 
	
		
			
				|  |  | +        material = el.components.material;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (results[0] > 0) {
 | 
	
		
			
				|  |  | -      results.forEach((r) => {
 | 
	
		
			
				|  |  | -        if (r < 0) convex = false;
 | 
	
		
			
				|  |  | -      });
 | 
	
		
			
				|  |  | -    } else {
 | 
	
		
			
				|  |  | -      results.forEach((r) => {
 | 
	
		
			
				|  |  | -        if (r > 0) convex = false;
 | 
	
		
			
				|  |  | +    var geometry = new THREE.PlaneGeometry(data.width, data.depth, data.density, data.density);
 | 
	
		
			
				|  |  | +    geometry.mergeVertices();
 | 
	
		
			
				|  |  | +    this.waves = [];
 | 
	
		
			
				|  |  | +    for (var v, i = 0, l = geometry.vertices.length; i < l; i++) {
 | 
	
		
			
				|  |  | +      v = geometry.vertices[i];
 | 
	
		
			
				|  |  | +      this.waves.push({
 | 
	
		
			
				|  |  | +        z: v.z,
 | 
	
		
			
				|  |  | +        ang: Math.random() * Math.PI * 2,
 | 
	
		
			
				|  |  | +        amp: data.amplitude + Math.random() * data.amplitudeVariance,
 | 
	
		
			
				|  |  | +        speed: (data.speed + Math.random() * data.speedVariance) / 1000 // radians / frame
 | 
	
		
			
				|  |  |        });
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    return convex;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  static distanceToSquared (a, b) {
 | 
	
		
			
				|  |  | +    if (!material) {
 | 
	
		
			
				|  |  | +      material = {};
 | 
	
		
			
				|  |  | +      material.material = new THREE.MeshPhongMaterial({
 | 
	
		
			
				|  |  | +        color: data.color,
 | 
	
		
			
				|  |  | +        transparent: data.opacity < 1,
 | 
	
		
			
				|  |  | +        opacity: data.opacity,
 | 
	
		
			
				|  |  | +        shading: THREE.FlatShading,
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    var dx = a.x - b.x;
 | 
	
		
			
				|  |  | -    var dy = a.y - b.y;
 | 
	
		
			
				|  |  | -    var dz = a.z - b.z;
 | 
	
		
			
				|  |  | +    this.mesh = new THREE.Mesh(geometry, material.material);
 | 
	
		
			
				|  |  | +    el.setObject3D('mesh', this.mesh);
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    return dx * dx + dy * dy + dz * dz;
 | 
	
		
			
				|  |  | +  remove: function () {
 | 
	
		
			
				|  |  | +    this.el.removeObject3D('mesh');
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +  tick: function (t, dt) {
 | 
	
		
			
				|  |  | +    if (!dt) return;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  //+ Jonas Raoni Soares Silva
 | 
	
		
			
				|  |  | -  //@ http://jsfromhell.com/math/is-point-in-poly [rev. #0]
 | 
	
		
			
				|  |  | -  static isPointInPoly (poly, pt) {
 | 
	
		
			
				|  |  | -    for (var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i)
 | 
	
		
			
				|  |  | -      ((poly[i].z <= pt.z && pt.z < poly[j].z) || (poly[j].z <= pt.z && pt.z < poly[i].z)) && (pt.x < (poly[j].x - poly[i].x) * (pt.z - poly[i].z) / (poly[j].z - poly[i].z) + poly[i].x) && (c = !c);
 | 
	
		
			
				|  |  | -    return c;
 | 
	
		
			
				|  |  | +    var verts = this.mesh.geometry.vertices;
 | 
	
		
			
				|  |  | +    for (var v, vprops, i = 0; (v = verts[i]); i++){
 | 
	
		
			
				|  |  | +      vprops = this.waves[i];
 | 
	
		
			
				|  |  | +      v.z = vprops.z + Math.sin(vprops.ang) * vprops.amp;
 | 
	
		
			
				|  |  | +      vprops.ang += vprops.speed * dt;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    this.mesh.geometry.verticesNeedUpdate = true;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  static isVectorInPolygon (vector, polygon, vertices) {
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    // reference point will be the centroid of the polygon
 | 
	
		
			
				|  |  | -    // We need to rotate the vector as well as all the points which the polygon uses
 | 
	
		
			
				|  |  | +module.exports.registerAll = (function () {
 | 
	
		
			
				|  |  | +  var registered = false;
 | 
	
		
			
				|  |  | +  return function (AFRAME) {
 | 
	
		
			
				|  |  | +    if (registered) return;
 | 
	
		
			
				|  |  | +    AFRAME = AFRAME || window.AFRAME;
 | 
	
		
			
				|  |  | +    AFRAME.registerComponent('ocean', Component);
 | 
	
		
			
				|  |  | +    AFRAME.registerPrimitive('a-ocean', Primitive);
 | 
	
		
			
				|  |  | +    registered = true;
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +}());
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    var lowestPoint = 100000;
 | 
	
		
			
				|  |  | -    var highestPoint = -100000;
 | 
	
		
			
				|  |  | +},{}],117:[function(require,module,exports){
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Tube following a custom path.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Usage:
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * ```html
 | 
	
		
			
				|  |  | + * <a-tube path="5 0 5, 5 0 -5, -5 0 -5" radius="0.5"></a-tube>
 | 
	
		
			
				|  |  | + * ```
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +var Primitive = module.exports.Primitive = {
 | 
	
		
			
				|  |  | +  defaultComponents: {
 | 
	
		
			
				|  |  | +    tube:           {},
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  | +  mappings: {
 | 
	
		
			
				|  |  | +    path:           'tube.path',
 | 
	
		
			
				|  |  | +    segments:       'tube.segments',
 | 
	
		
			
				|  |  | +    radius:         'tube.radius',
 | 
	
		
			
				|  |  | +    radialSegments: 'tube.radialSegments',
 | 
	
		
			
				|  |  | +    closed:         'tube.closed'
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    var polygonVertices = [];
 | 
	
		
			
				|  |  | +var Component = module.exports.Component = {
 | 
	
		
			
				|  |  | +  schema: {
 | 
	
		
			
				|  |  | +    path:           {default: []},
 | 
	
		
			
				|  |  | +    segments:       {default: 64},
 | 
	
		
			
				|  |  | +    radius:         {default: 1},
 | 
	
		
			
				|  |  | +    radialSegments: {default: 8},
 | 
	
		
			
				|  |  | +    closed:         {default: false}
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    polygon.vertexIds.forEach((vId) => {
 | 
	
		
			
				|  |  | -      lowestPoint = Math.min(vertices[vId].y, lowestPoint);
 | 
	
		
			
				|  |  | -      highestPoint = Math.max(vertices[vId].y, highestPoint);
 | 
	
		
			
				|  |  | -      polygonVertices.push(vertices[vId]);
 | 
	
		
			
				|  |  | -    });
 | 
	
		
			
				|  |  | +  init: function () {
 | 
	
		
			
				|  |  | +    var el = this.el,
 | 
	
		
			
				|  |  | +        data = this.data,
 | 
	
		
			
				|  |  | +        material = el.components.material;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (vector.y < highestPoint + 0.5 && vector.y > lowestPoint - 0.5 &&
 | 
	
		
			
				|  |  | -      this.isPointInPoly(polygonVertices, vector)) {
 | 
	
		
			
				|  |  | -      return true;
 | 
	
		
			
				|  |  | +    if (!data.path.length) {
 | 
	
		
			
				|  |  | +      console.error('[a-tube] `path` property expected but not found.');
 | 
	
		
			
				|  |  | +      return;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -    return false;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  static triarea2 (a, b, c) {
 | 
	
		
			
				|  |  | -    var ax = b.x - a.x;
 | 
	
		
			
				|  |  | -    var az = b.z - a.z;
 | 
	
		
			
				|  |  | -    var bx = c.x - a.x;
 | 
	
		
			
				|  |  | -    var bz = c.z - a.z;
 | 
	
		
			
				|  |  | -    return bx * az - ax * bz;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  static vequal (a, b) {
 | 
	
		
			
				|  |  | -    return this.distanceToSquared(a, b) < 0.00001;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +    var curve = new THREE.CatmullRomCurve3(data.path.map(function (point) {
 | 
	
		
			
				|  |  | +      point = point.split(' ');
 | 
	
		
			
				|  |  | +      return new THREE.Vector3(Number(point[0]), Number(point[1]), Number(point[2]));
 | 
	
		
			
				|  |  | +    }));
 | 
	
		
			
				|  |  | +    var geometry = new THREE.TubeGeometry(
 | 
	
		
			
				|  |  | +      curve, data.segments, data.radius, data.radialSegments, data.closed
 | 
	
		
			
				|  |  | +    );
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  static array_intersect () {
 | 
	
		
			
				|  |  | -    let i, shortest, nShortest, n, len, ret = [],
 | 
	
		
			
				|  |  | -      obj = {},
 | 
	
		
			
				|  |  | -      nOthers;
 | 
	
		
			
				|  |  | -    nOthers = arguments.length - 1;
 | 
	
		
			
				|  |  | -    nShortest = arguments[0].length;
 | 
	
		
			
				|  |  | -    shortest = 0;
 | 
	
		
			
				|  |  | -    for (i = 0; i <= nOthers; i++) {
 | 
	
		
			
				|  |  | -      n = arguments[i].length;
 | 
	
		
			
				|  |  | -      if (n < nShortest) {
 | 
	
		
			
				|  |  | -        shortest = i;
 | 
	
		
			
				|  |  | -        nShortest = n;
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | +    if (!material) {
 | 
	
		
			
				|  |  | +      material = {};
 | 
	
		
			
				|  |  | +      material.material = new THREE.MeshPhongMaterial();
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    for (i = 0; i <= nOthers; i++) {
 | 
	
		
			
				|  |  | -      n = (i === shortest) ? 0 : (i || shortest); //Read the shortest array first. Read the first array instead of the shortest
 | 
	
		
			
				|  |  | -      len = arguments[n].length;
 | 
	
		
			
				|  |  | -      for (var j = 0; j < len; j++) {
 | 
	
		
			
				|  |  | -        var elem = arguments[n][j];
 | 
	
		
			
				|  |  | -        if (obj[elem] === i - 1) {
 | 
	
		
			
				|  |  | -          if (i === nOthers) {
 | 
	
		
			
				|  |  | -            ret.push(elem);
 | 
	
		
			
				|  |  | -            obj[elem] = 0;
 | 
	
		
			
				|  |  | -          } else {
 | 
	
		
			
				|  |  | -            obj[elem] = i;
 | 
	
		
			
				|  |  | -          }
 | 
	
		
			
				|  |  | -        } else if (i === 0) {
 | 
	
		
			
				|  |  | -          obj[elem] = 0;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    return ret;
 | 
	
		
			
				|  |  | +    this.mesh = new THREE.Mesh(geometry, material.material);
 | 
	
		
			
				|  |  | +    this.el.setObject3D('mesh', this.mesh);
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  remove: function () {
 | 
	
		
			
				|  |  | +    if (this.mesh) this.el.removeObject3D('mesh');
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +module.exports.registerAll = (function () {
 | 
	
		
			
				|  |  | +  var registered = false;
 | 
	
		
			
				|  |  | +  return function (AFRAME) {
 | 
	
		
			
				|  |  | +    if (registered) return;
 | 
	
		
			
				|  |  | +    AFRAME = AFRAME || window.AFRAME;
 | 
	
		
			
				|  |  | +    AFRAME.registerComponent('tube', Component);
 | 
	
		
			
				|  |  | +    AFRAME.registerPrimitive('a-tube', Primitive);
 | 
	
		
			
				|  |  | +    registered = true;
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +}());
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +},{}],118:[function(require,module,exports){
 | 
	
		
			
				|  |  | +module.exports = {
 | 
	
		
			
				|  |  | +  'a-grid':     require('./a-grid'),
 | 
	
		
			
				|  |  | +  'a-hexgrid': require('./a-hexgrid'),
 | 
	
		
			
				|  |  | +  'a-ocean':    require('./a-ocean'),
 | 
	
		
			
				|  |  | +  'a-tube':     require('./a-tube'),
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -module.exports = Utils;
 | 
	
		
			
				|  |  | +  registerAll: function (AFRAME) {
 | 
	
		
			
				|  |  | +    if (this._registered) return;
 | 
	
		
			
				|  |  | +    AFRAME = AFRAME || window.AFRAME;
 | 
	
		
			
				|  |  | +    this['a-grid'].registerAll(AFRAME);
 | 
	
		
			
				|  |  | +    this['a-hexgrid'].registerAll(AFRAME);
 | 
	
		
			
				|  |  | +    this['a-ocean'].registerAll(AFRAME);
 | 
	
		
			
				|  |  | +    this['a-tube'].registerAll(AFRAME);
 | 
	
		
			
				|  |  | +    this._registered = true;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -},{}]},{},[1]);
 | 
	
		
			
				|  |  | +},{"./a-grid":114,"./a-hexgrid":115,"./a-ocean":116,"./a-tube":117}]},{},[1]);
 |