UNPKG

ggabcd-meshwalk

Version:

MeshWalk.js is a JS library which helps your TPS game development with three.js.

3,173 lines 123 kB
(function (global, factory) {
	typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
	typeof define === 'function' && define.amd ? define(factory) :
	(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.MW = factory());
})(this, (function () { 'use strict';

	var THREE$1; // bind on install

	var onInstallHandlers = [];
	function install(_THREE) {
	  if (THREE$1 && _THREE === THREE$1) {
	    console.error("[THREE] already installed. `install` should be called only once.");
	    return;
	  }

	  THREE$1 = _THREE;
	  onInstallHandlers.forEach(function (handler) {
	    return handler();
	  });
	}

	function _classCallCheck(instance, Constructor) {
	  if (!(instance instanceof Constructor)) {
	    throw new TypeError("Cannot call a class as a function");
	  }
	}

	function _defineProperties(target, props) {
	  for (var i = 0; i < props.length; i++) {
	    var descriptor = props[i];
	    descriptor.enumerable = descriptor.enumerable || false;
	    descriptor.configurable = true;
	    if ("value" in descriptor) descriptor.writable = true;
	    Object.defineProperty(target, descriptor.key, descriptor);
	  }
	}

	function _createClass(Constructor, protoProps, staticProps) {
	  if (protoProps) _defineProperties(Constructor.prototype, protoProps);
	  if (staticProps) _defineProperties(Constructor, staticProps);
	  Object.defineProperty(Constructor, "prototype", {
	    writable: false
	  });
	  return Constructor;
	}

	function _inherits(subClass, superClass) {
	  if (typeof superClass !== "function" && superClass !== null) {
	    throw new TypeError("Super expression must either be null or a function");
	  }

	  subClass.prototype = Object.create(superClass && superClass.prototype, {
	    constructor: {
	      value: subClass,
	      writable: true,
	      configurable: true
	    }
	  });
	  Object.defineProperty(subClass, "prototype", {
	    writable: false
	  });
	  if (superClass) _setPrototypeOf(subClass, superClass);
	}

	function _getPrototypeOf(o) {
	  _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
	    return o.__proto__ || Object.getPrototypeOf(o);
	  };
	  return _getPrototypeOf(o);
	}

	function _setPrototypeOf(o, p) {
	  _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
	    o.__proto__ = p;
	    return o;
	  };

	  return _setPrototypeOf(o, p);
	}

	function _isNativeReflectConstruct() {
	  if (typeof Reflect === "undefined" || !Reflect.construct) return false;
	  if (Reflect.construct.sham) return false;
	  if (typeof Proxy === "function") return true;

	  try {
	    Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
	    return true;
	  } catch (e) {
	    return false;
	  }
	}

	function _assertThisInitialized(self) {
	  if (self === void 0) {
	    throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
	  }

	  return self;
	}

	function _possibleConstructorReturn(self, call) {
	  if (call && (typeof call === "object" || typeof call === "function")) {
	    return call;
	  } else if (call !== void 0) {
	    throw new TypeError("Derived constructors may only return object or undefined");
	  }

	  return _assertThisInitialized(self);
	}

	function _createSuper(Derived) {
	  var hasNativeReflectConstruct = _isNativeReflectConstruct();

	  return function _createSuperInternal() {
	    var Super = _getPrototypeOf(Derived),
	        result;

	    if (hasNativeReflectConstruct) {
	      var NewTarget = _getPrototypeOf(this).constructor;

	      result = Reflect.construct(Super, arguments, NewTarget);
	    } else {
	      result = Super.apply(this, arguments);
	    }

	    return _possibleConstructorReturn(this, result);
	  };
	}

	function _superPropBase(object, property) {
	  while (!Object.prototype.hasOwnProperty.call(object, property)) {
	    object = _getPrototypeOf(object);
	    if (object === null) break;
	  }

	  return object;
	}

	function _get() {
	  if (typeof Reflect !== "undefined" && Reflect.get) {
	    _get = Reflect.get;
	  } else {
	    _get = function _get(target, property, receiver) {
	      var base = _superPropBase(target, property);

	      if (!base) return;
	      var desc = Object.getOwnPropertyDescriptor(base, property);

	      if (desc.get) {
	        return desc.get.call(arguments.length < 3 ? target : receiver);
	      }

	      return desc.value;
	    };
	  }

	  return _get.apply(this, arguments);
	}

	var vec3;
	var vec3_0;
	var vec3_1;
	var center;
	var extents;
	onInstallHandlers.push(function () {
	  vec3 = new THREE$1.Vector3();
	  vec3_0 = new THREE$1.Vector3();
	  vec3_1 = new THREE$1.Vector3();
	  center = new THREE$1.Vector3();
	  extents = new THREE$1.Vector3();
	}); // aabb: <THREE.Box3>
	// Plane: <THREE.Plane>

	function isIntersectionAABBPlane(aabb, Plane) {
	  center.addVectors(aabb.max, aabb.min).multiplyScalar(0.5);
	  extents.subVectors(aabb.max, center);
	  var r = extents.x * Math.abs(Plane.normal.x) + extents.y * Math.abs(Plane.normal.y) + extents.z * Math.abs(Plane.normal.z);
	  var s = Plane.normal.dot(center) - Plane.constant;
	  return Math.abs(s) <= r;
	}
	var v0;
	var v1;
	var v2;
	var f0;
	var f1;
	var f2;
	var a00;
	var a01;
	var a02;
	var a10;
	var a11;
	var a12;
	var a20;
	var a21;
	var a22;
	var plane;
	onInstallHandlers.push(function () {
	  v0 = new THREE$1.Vector3();
	  v1 = new THREE$1.Vector3();
	  v2 = new THREE$1.Vector3();
	  f0 = new THREE$1.Vector3();
	  f1 = new THREE$1.Vector3();
	  f2 = new THREE$1.Vector3();
	  a00 = new THREE$1.Vector3();
	  a01 = new THREE$1.Vector3();
	  a02 = new THREE$1.Vector3();
	  a10 = new THREE$1.Vector3();
	  a11 = new THREE$1.Vector3();
	  a12 = new THREE$1.Vector3();
	  a20 = new THREE$1.Vector3();
	  a21 = new THREE$1.Vector3();
	  a22 = new THREE$1.Vector3();
	  plane = new THREE$1.Plane();
	}); // based on http://www.gamedev.net/topic/534655-aabb-triangleplane-intersection--distance-to-plane-is-incorrect-i-have-solved-it/
	//
	// a: <THREE.Vector3>, // vertex of a triangle
	// b: <THREE.Vector3>, // vertex of a triangle
	// c: <THREE.Vector3>, // vertex of a triangle
	// aabb: <THREE.Box3>

	function isIntersectionTriangleAABB(a, b, c, aabb) {
	  var p0, p1, p2, r; // Compute box center and extents of AABoundingBox (if not already given in that format)

	  center.addVectors(aabb.max, aabb.min).multiplyScalar(0.5);
	  extents.subVectors(aabb.max, center); // Translate triangle as conceptually moving AABB to origin

	  v0.subVectors(a, center);
	  v1.subVectors(b, center);
	  v2.subVectors(c, center); // Compute edge vectors for triangle

	  f0.subVectors(v1, v0);
	  f1.subVectors(v2, v1);
	  f2.subVectors(v0, v2); // Test axes a00..a22 (category 3)

	  a00.set(0, -f0.z, f0.y);
	  a01.set(0, -f1.z, f1.y);
	  a02.set(0, -f2.z, f2.y);
	  a10.set(f0.z, 0, -f0.x);
	  a11.set(f1.z, 0, -f1.x);
	  a12.set(f2.z, 0, -f2.x);
	  a20.set(-f0.y, f0.x, 0);
	  a21.set(-f1.y, f1.x, 0);
	  a22.set(-f2.y, f2.x, 0); // Test axis a00

	  p0 = v0.dot(a00);
	  p1 = v1.dot(a00);
	  p2 = v2.dot(a00);
	  r = extents.y * Math.abs(f0.z) + extents.z * Math.abs(f0.y);

	  if (Math.max(-Math.max(p0, p1, p2), Math.min(p0, p1, p2)) > r) {
	    return false; // Axis is a separating axis
	  } // Test axis a01


	  p0 = v0.dot(a01);
	  p1 = v1.dot(a01);
	  p2 = v2.dot(a01);
	  r = extents.y * Math.abs(f1.z) + extents.z * Math.abs(f1.y);

	  if (Math.max(-Math.max(p0, p1, p2), Math.min(p0, p1, p2)) > r) {
	    return false; // Axis is a separating axis
	  } // Test axis a02


	  p0 = v0.dot(a02);
	  p1 = v1.dot(a02);
	  p2 = v2.dot(a02);
	  r = extents.y * Math.abs(f2.z) + extents.z * Math.abs(f2.y);

	  if (Math.max(-Math.max(p0, p1, p2), Math.min(p0, p1, p2)) > r) {
	    return false; // Axis is a separating axis
	  } // Test axis a10


	  p0 = v0.dot(a10);
	  p1 = v1.dot(a10);
	  p2 = v2.dot(a10);
	  r = extents.x * Math.abs(f0.z) + extents.z * Math.abs(f0.x);

	  if (Math.max(-Math.max(p0, p1, p2), Math.min(p0, p1, p2)) > r) {
	    return false; // Axis is a separating axis
	  } // Test axis a11


	  p0 = v0.dot(a11);
	  p1 = v1.dot(a11);
	  p2 = v2.dot(a11);
	  r = extents.x * Math.abs(f1.z) + extents.z * Math.abs(f1.x);

	  if (Math.max(-Math.max(p0, p1, p2), Math.min(p0, p1, p2)) > r) {
	    return false; // Axis is a separating axis
	  } // Test axis a12


	  p0 = v0.dot(a12);
	  p1 = v1.dot(a12);
	  p2 = v2.dot(a12);
	  r = extents.x * Math.abs(f2.z) + extents.z * Math.abs(f2.x);

	  if (Math.max(-Math.max(p0, p1, p2), Math.min(p0, p1, p2)) > r) {
	    return false; // Axis is a separating axis
	  } // Test axis a20


	  p0 = v0.dot(a20);
	  p1 = v1.dot(a20);
	  p2 = v2.dot(a20);
	  r = extents.x * Math.abs(f0.y) + extents.y * Math.abs(f0.x);

	  if (Math.max(-Math.max(p0, p1, p2), Math.min(p0, p1, p2)) > r) {
	    return false; // Axis is a separating axis
	  } // Test axis a21


	  p0 = v0.dot(a21);
	  p1 = v1.dot(a21);
	  p2 = v2.dot(a21);
	  r = extents.x * Math.abs(f1.y) + extents.y * Math.abs(f1.x);

	  if (Math.max(-Math.max(p0, p1, p2), Math.min(p0, p1, p2)) > r) {
	    return false; // Axis is a separating axis
	  } // Test axis a22


	  p0 = v0.dot(a22);
	  p1 = v1.dot(a22);
	  p2 = v2.dot(a22);
	  r = extents.x * Math.abs(f2.y) + extents.y * Math.abs(f2.x);

	  if (Math.max(-Math.max(p0, p1, p2), Math.min(p0, p1, p2)) > r) {
	    return false; // Axis is a separating axis
	  } // Test the three axes corresponding to the face normals of AABB b (category 1).
	  // Exit if...
	  // ... [-extents.x, extents.x] and [min(v0.x,v1.x,v2.x), max(v0.x,v1.x,v2.x)] do not overlap


	  if (Math.max(v0.x, v1.x, v2.x) < -extents.x || Math.min(v0.x, v1.x, v2.x) > extents.x) {
	    return false;
	  } // ... [-extents.y, extents.y] and [min(v0.y,v1.y,v2.y), max(v0.y,v1.y,v2.y)] do not overlap


	  if (Math.max(v0.y, v1.y, v2.y) < -extents.y || Math.min(v0.y, v1.y, v2.y) > extents.y) {
	    return false;
	  } // ... [-extents.z, extents.z] and [min(v0.z,v1.z,v2.z), max(v0.z,v1.z,v2.z)] do not overlap


	  if (Math.max(v0.z, v1.z, v2.z) < -extents.z || Math.min(v0.z, v1.z, v2.z) > extents.z) {
	    return false;
	  } // Test separating axis corresponding to triangle face normal (category 2)
	  // Face Normal is -ve as Triangle is clockwise winding (and XNA uses -z for into screen)


	  plane.normal.copy(f1).cross(f0).normalize();
	  plane.constant = plane.normal.dot(a);
	  return isIntersectionAABBPlane(aabb, plane);
	} // sphere1: <THREE.Sphere>
	// sphere: <THREE.Sphere>
	// aabb: <THREE.Box3>

	function isIntersectionSphereAABB(sphere, aabb) {
	  var sqDist = 0;
	  if (sphere.center.x < aabb.min.x) sqDist += (aabb.min.x - sphere.center.x) * (aabb.min.x - sphere.center.x);
	  if (sphere.center.x > aabb.max.x) sqDist += (sphere.center.x - aabb.max.x) * (sphere.center.x - aabb.max.x);
	  if (sphere.center.y < aabb.min.y) sqDist += (aabb.min.y - sphere.center.y) * (aabb.min.y - sphere.center.y);
	  if (sphere.center.y > aabb.max.y) sqDist += (sphere.center.y - aabb.max.y) * (sphere.center.y - aabb.max.y);
	  if (sphere.center.z < aabb.min.z) sqDist += (aabb.min.z - sphere.center.z) * (aabb.min.z - sphere.center.z);
	  if (sphere.center.z > aabb.max.z) sqDist += (sphere.center.z - aabb.max.z) * (sphere.center.z - aabb.max.z);
	  return sqDist <= sphere.radius * sphere.radius;
	}
	var A;
	var B;
	var C;
	var V;
	var AB;
	var BC;
	var CA;
	var Q1;
	var Q2;
	var Q3;
	var QC;
	var QA;
	var QB;
	var negatedNormal;
	onInstallHandlers.push(function () {
	  A = new THREE$1.Vector3();
	  B = new THREE$1.Vector3();
	  C = new THREE$1.Vector3();
	  V = new THREE$1.Vector3();
	  AB = new THREE$1.Vector3();
	  BC = new THREE$1.Vector3();
	  CA = new THREE$1.Vector3();
	  Q1 = new THREE$1.Vector3();
	  Q2 = new THREE$1.Vector3();
	  Q3 = new THREE$1.Vector3();
	  QC = new THREE$1.Vector3();
	  QA = new THREE$1.Vector3();
	  QB = new THREE$1.Vector3();
	  negatedNormal = new THREE$1.Vector3();
	}); //http://clb.demon.fi/MathGeoLib/docs/Triangle.cpp_code.html#459
	// sphere: <THREE.Sphere>
	// a: <THREE.Vector3>, // vertex of a triangle
	// b: <THREE.Vector3>, // vertex of a triangle
	// c: <THREE.Vector3>, // vertex of a triangle
	// normal: <THREE.Vector3>, // normal of a triangle

	function isIntersectionSphereTriangle(sphere, a, b, c, normal) {
	  // http://realtimecollisiondetection.net/blog/?p=103
	  // vs plain of triangle face
	  A.subVectors(a, sphere.center);
	  B.subVectors(b, sphere.center);
	  C.subVectors(c, sphere.center);
	  var rr = sphere.radius * sphere.radius;
	  V.crossVectors(vec3_0.subVectors(B, A), vec3_1.subVectors(C, A));
	  var d = A.dot(V);
	  var e = V.dot(V);

	  if (d * d > rr * e) {
	    return false;
	  } // vs triangle vertex


	  var aa = A.dot(A);
	  var ab = A.dot(B);
	  var ac = A.dot(C);
	  var bb = B.dot(B);
	  var bc = B.dot(C);
	  var cc = C.dot(C);

	  if (aa > rr & ab > aa & ac > aa || bb > rr & ab > bb & bc > bb || cc > rr & ac > cc & bc > cc) {
	    return false;
	  } // vs edge


	  AB.subVectors(B, A);
	  BC.subVectors(C, B);
	  CA.subVectors(A, C);
	  var d1 = ab - aa;
	  var d2 = bc - bb;
	  var d3 = ac - cc;
	  var e1 = AB.dot(AB);
	  var e2 = BC.dot(BC);
	  var e3 = CA.dot(CA);
	  Q1.subVectors(A.multiplyScalar(e1), AB.multiplyScalar(d1));
	  Q2.subVectors(B.multiplyScalar(e2), BC.multiplyScalar(d2));
	  Q3.subVectors(C.multiplyScalar(e3), CA.multiplyScalar(d3));
	  QC.subVectors(C.multiplyScalar(e1), Q1);
	  QA.subVectors(A.multiplyScalar(e2), Q2);
	  QB.subVectors(B.multiplyScalar(e3), Q3);

	  if (Q1.dot(Q1) > rr * e1 * e1 && Q1.dot(QC) >= 0 || Q2.dot(Q2) > rr * e2 * e2 && Q2.dot(QA) >= 0 || Q3.dot(Q3) > rr * e3 * e3 && Q3.dot(QB) >= 0) {
	    return false;
	  }

	  var distance = Math.sqrt(d * d / e) - sphere.radius - 1;
	  negatedNormal.set(-normal.x, -normal.y, -normal.z);
	  var contactPoint = sphere.center.clone().add(negatedNormal.multiplyScalar(distance));
	  return {
	    distance: distance,
	    contactPoint: contactPoint
	  };
	} // based on Real-Time Collision Detection Section 5.3.4
	// p: <THREE.Vector3>, // line3.start
	// q: <THREE.Vector3>, // line3.end
	// a: <THREE.Vector3>, // triangle.a
	// b: <THREE.Vector3>, // triangle.b
	// c: <THREE.Vector3>, // triangle.c
	// normal: <THREE.Vector3>, // triangle.normal, optional
	// var scalarTriple = function ( a, b, c ) {
	//   var m = b.clone().cross( c );
	//   return a.dot( m );
	// }
	// var vectorTriple = function ( a, b, c ) {
	//   var m = b.clone().cross( c );
	//   return a.clone().cross( m );
	// }
	// export function isIntersectionLineTrianglefunction ( p, q, a, b, c, precisio{
	//   var pq = q.clone().sub( p ),
	//       pa = a.clone().sub( p ),
	//       pb = b.clone().sub( p ),
	//       pc = c.clone().sub( p ),
	//       u, v, w;
	//   u = scalarTriple( pq, pc, pb );
	//   if ( u < 0 ) { return false; }
	//   v = scalarTriple( pq, pa, pc );
	//   if ( v < 0 ) { return false; }
	//   w = scalarTriple( pq, pb, pa );
	//   if ( w < 0 ) { return false; }
	//   var denom = 1 / ( u + v + w );
	//   u *= denom;
	//   v *= denom;
	//   w *= denom;
	//   var au = a.clone().multiplyScalar( u ),
	//       bv = b.clone().multiplyScalar( v ),
	//       cw = c.clone().multiplyScalar( w ),
	//       contactPoint = au.clone().add( bv ).add( cw );
	//   return {
	//     contactPoint: contactPoint
	//   }
	// }

	var ab;
	var ac;
	var qp;
	var n;
	var ap;
	var e;
	var au;
	var bv;
	var cw;
	onInstallHandlers.push(function () {
	  ab = new THREE$1.Vector3();
	  ac = new THREE$1.Vector3();
	  qp = new THREE$1.Vector3();
	  n = new THREE$1.Vector3();
	  ap = new THREE$1.Vector3();
	  e = new THREE$1.Vector3();
	  au = new THREE$1.Vector3();
	  bv = new THREE$1.Vector3();
	  cw = new THREE$1.Vector3();
	});
	function testSegmentTriangle(p, q, a, b, c) {
	  ab.subVectors(b, a);
	  ac.subVectors(c, a);
	  qp.subVectors(p, q);
	  n.copy(ab).cross(ac);
	  var d = qp.dot(n);
	  if (d <= 0) return false;
	  ap.subVectors(p, a);
	  var t = ap.dot(n);
	  if (t < 0) return 0;
	  if (t > d) return 0;
	  e.copy(qp).cross(ap);
	  var v = ac.dot(e);
	  if (v < 0 || v > d) return 0;
	  var w = vec3.copy(ab).dot(e) * -1;
	  if (w < 0 || v + w > d) return 0;
	  var ood = 1 / d;
	  t *= ood;
	  v *= ood;
	  w *= ood;
	  var u = 1 - v - w;
	  au.copy(a).multiplyScalar(u);
	  bv.copy(b).multiplyScalar(v);
	  cw.copy(c).multiplyScalar(w);
	  var contactPoint = au.clone().add(bv).add(cw);
	  return {
	    contactPoint: contactPoint
	  };
	}

	// based on http://marupeke296.com/COL_3D_No15_Octree.html
	//
	//       +------+------+
	//       |\   2  \   3  \
	//       | +------+------+
	//       + |\      \      \
	//       |\| +------+------+
	//       | + |      |      |
	//       +0|\|   6  |   7  |
	//        \| +------+------+
	//         + |      |      |
	//    y     \|   4  |   5  |
	//    |      +------+------+
	//    +--x
	//     \
	//      z
	//
	//
	//       +------+------+
	//       |\   6  \   7  \
	//       | +------+------+
	//       + |\      \      \
	//       |\| +------+------+
	//       | + |      |      |
	//       +4|\|   2  |   3  |
	//        \| +------+------+
	//         + |      |      |
	//  z y     \|   0  |   1  |
	//   \|      +------+------+
	//    +--x
	//
	// min: <THREE.Vector3>
	// max: <THREE.Vector3>
	// maxDepth: <Number>

	var Octree = /*#__PURE__*/function () {
	  function Octree(min, max, maxDepth) {
	    _classCallCheck(this, Octree);

	    this.min = min;
	    this.max = max;
	    this.maxDepth = maxDepth;
	    this.nodes = [];
	    this.isOctree = true;
	    var nodeBoxSize = new THREE$1.Vector3();
	    var nodeBoxMin = new THREE$1.Vector3();
	    var nodeBoxMax = new THREE$1.Vector3();

	    for (var depth = 0; depth < this.maxDepth; depth++) {
	      this.nodes.push([]);
	      var pow2 = Math.pow(2, depth);
	      var pow4 = Math.pow(4, depth);
	      nodeBoxSize.subVectors(this.max, this.min).divideScalar(pow2);

	      for (var i = 0, length = Math.pow(8, depth); i < length; i++) {
	        var indexX = i % pow2;
	        var indexY = i / pow4 | 0;
	        var indexZ = (i / pow2 | 0) % pow2;
	        nodeBoxMin.set(this.min.x + indexX * nodeBoxSize.x, this.min.y + indexY * nodeBoxSize.y, this.min.z + indexZ * nodeBoxSize.z);
	        nodeBoxMax.copy(nodeBoxMin).add(nodeBoxSize);
	        var mortonNumber = Octree.getMortonNumber(indexX, indexY, indexZ);
	        this.nodes[depth][mortonNumber] = new OctreeNode(this, depth, mortonNumber, nodeBoxMin, nodeBoxMax);
	      }
	    }
	  }

	  _createClass(Octree, [{
	    key: "importThreeMesh",
	    value: function importThreeMesh(threeMesh) {
	      threeMesh.updateMatrix();
	      var geometryId = threeMesh.geometry.uuid;
	      var geometry = threeMesh.geometry.clone();
	      geometry.applyMatrix4(threeMesh.matrix);
	      geometry.computeVertexNormals();

	      if (geometry instanceof THREE$1.BufferGeometry) {
	        if (geometry.index !== undefined) {
	          var indices = geometry.index.array;
	          var positions = geometry.attributes.position.array; // const normals   = geometry.attributes.normal.array;

	          var offsets = geometry.groups.length !== 0 ? geometry.groups : [{
	            start: 0,
	            count: indices.length,
	            materialIndex: 0
	          }];

	          for (var i = 0, l = offsets.length; i < l; ++i) {
	            var start = offsets[i].start;
	            var count = offsets[i].count;
	            var index = offsets[i].materialIndex;

	            for (var ii = start, ll = start + count; ii < ll; ii += 3) {
	              var a = index + indices[ii];
	              var b = index + indices[ii + 1];
	              var c = index + indices[ii + 2];
	              var vA = new THREE$1.Vector3().fromArray(positions, a * 3);
	              var vB = new THREE$1.Vector3().fromArray(positions, b * 3);
	              var vC = new THREE$1.Vector3().fromArray(positions, c * 3); // https://github.com/mrdoob/three.js/issues/4691
	              // make face normal

	              var cb = new THREE$1.Vector3().subVectors(vC, vB);
	              var ab = new THREE$1.Vector3().subVectors(vA, vB);
	              var faceNormal = cb.cross(ab).normalize().clone();
	              var face = new Face(vA, vB, vC, faceNormal, geometryId);
	              this.addFace(face);
	            }
	          }
	        }

	        return;
	      }

	      geometry.computeFaceNormals();

	      for (var _i = 0, _l = geometry.faces.length; _i < _l; _i++) {
	        var _face = new Face(geometry.vertices[geometry.faces[_i].a], geometry.vertices[geometry.faces[_i].b], geometry.vertices[geometry.faces[_i].c], geometry.faces[_i].normal, geometryId);

	        this.addFace(_face);
	      }
	    }
	  }, {
	    key: "addFace",
	    value: function addFace(face) {
	      var tmp = [];
	      var targetNodes = this.nodes[0].slice(0);

	      for (var i = 0, l = this.maxDepth; i < l; i++) {
	        for (var ii = 0, ll = targetNodes.length; ii < ll; ii++) {
	          var node = targetNodes[ii];
	          var isIntersected = isIntersectionTriangleAABB(face.a, face.b, face.c, node);

	          if (isIntersected) {
	            node.trianglePool.push(face);

	            if (i + 1 !== this.maxDepth) {
	              tmp = tmp.concat(node.getChildNodes());
	            }
	          }
	        }

	        if (tmp.length === 0) {
	          break;
	        }

	        targetNodes = tmp.slice(0);
	        tmp.length = 0;
	      }
	    }
	  }, {
	    key: "removeThreeMesh",
	    value: function removeThreeMesh(meshID) {
	      this.nodes.forEach(function (nodeDepth) {
	        nodeDepth.forEach(function (node) {
	          var newTrianglePool = [];
	          node.trianglePool.forEach(function (face) {
	            if (face.meshID !== meshID) {
	              newTrianglePool.push(face);
	            }
	          });
	          node.trianglePool = newTrianglePool;
	        });
	      });
	    }
	  }, {
	    key: "getIntersectedNodes",
	    value: function getIntersectedNodes(sphere, depth) {
	      var tmp = [];
	      var intersectedNodes = [];
	      var isIntersected = isIntersectionSphereAABB(sphere, this);
	      if (!isIntersected) return [];
	      var targetNodes = this.nodes[0].slice(0);

	      for (var i = 0, l = depth; i < l; i++) {
	        for (var ii = 0, ll = targetNodes.length; ii < ll; ii++) {
	          var node = targetNodes[ii];

	          var _isIntersected = isIntersectionSphereAABB(sphere, node);

	          if (_isIntersected) {
	            var isAtMaxDepth = i + 1 === depth;

	            if (isAtMaxDepth) {
	              if (node.trianglePool.length !== 0) {
	                intersectedNodes.push(node);
	              }
	            } else {
	              tmp = tmp.concat(node.getChildNodes());
	            }
	          }
	        }

	        targetNodes = tmp.slice(0);
	        tmp.length = 0;
	      }

	      return intersectedNodes;
	    }
	  }]);

	  return Octree;
	}();

	Octree.separate3Bit = function (n) {
	  n = (n | n << 8) & 0x0000f00f;
	  n = (n | n << 4) & 0x000c30c3;
	  n = (n | n << 2) & 0x00249249;
	  return n;
	};

	Octree.getMortonNumber = function (x, y, z) {
	  return Octree.separate3Bit(x) | Octree.separate3Bit(y) << 1 | Octree.separate3Bit(z) << 2;
	};

	Octree.uniqTrianglesFromNodes = function (nodes) {
	  var uniq = [];
	  var isContained = false;
	  if (nodes.length === 0) return [];
	  if (nodes.length === 1) return nodes[0].trianglePool.slice(0);

	  for (var i = 0, l = nodes.length; i < l; i++) {
	    for (var ii = 0, ll = nodes[i].trianglePool.length; ii < ll; ii++) {
	      for (var iii = 0, lll = uniq.length; iii < lll; iii++) {
	        if (nodes[i].trianglePool[ii] === uniq[iii]) {
	          isContained = true;
	        }
	      }

	      if (!isContained) {
	        uniq.push(nodes[i].trianglePool[ii]);
	      }

	      isContained = false;
	    }
	  }

	  return uniq;
	}; //


	var OctreeNode = /*#__PURE__*/function () {
	  function OctreeNode(tree, depth, mortonNumber, min, max) {
	    _classCallCheck(this, OctreeNode);

	    this.tree = tree;
	    this.depth = depth;
	    this.mortonNumber = mortonNumber;
	    this.min = new THREE$1.Vector3(min.x, min.y, min.z);
	    this.max = new THREE$1.Vector3(max.x, max.y, max.z);
	    this.trianglePool = [];
	  }

	  _createClass(OctreeNode, [{
	    key: "getParentNode",
	    value: function getParentNode() {
	      if (this.depth === 0) return null;
	      this.tree.nodes[this.depth][this.mortonNumber >> 3];
	    }
	  }, {
	    key: "getChildNodes",
	    value: function getChildNodes() {
	      if (this.tree.maxDepth === this.depth) {
	        return null;
	      }

	      var firstChild = this.mortonNumber << 3;
	      return [this.tree.nodes[this.depth + 1][firstChild], this.tree.nodes[this.depth + 1][firstChild + 1], this.tree.nodes[this.depth + 1][firstChild + 2], this.tree.nodes[this.depth + 1][firstChild + 3], this.tree.nodes[this.depth + 1][firstChild + 4], this.tree.nodes[this.depth + 1][firstChild + 5], this.tree.nodes[this.depth + 1][firstChild + 6], this.tree.nodes[this.depth + 1][firstChild + 7]];
	    }
	  }]);

	  return OctreeNode;
	}(); //
	// a: <THREE.Vector3>
	// b: <THREE.Vector3>
	// c: <THREE.Vector3>
	// normal: <THREE.Vector3>
	// meshID: <String>


	var Face = /*#__PURE__*/_createClass(function Face(a, b, c, normal, meshID) {
	  _classCallCheck(this, Face);

	  this.a = a.clone();
	  this.b = b.clone();
	  this.c = c.clone();
	  this.normal = normal.clone();
	  this.meshID = meshID;
	}); // origin   : <THREE.Vector3>
	// direction: <THREE.Vector3>
	// distance : <Float>
	// class Ray{
	// 	constructor( origin, direction, distance ) {
	// 		this.origin = origin;
	// 		this.direction = direction;
	// 		this.distance = distance;
	// 	}
	// }

	var sphere;
	onInstallHandlers.push(function () {
	  sphere = new THREE$1.Sphere();
	});
	var World = /*#__PURE__*/function () {
	  function World() {
	    _classCallCheck(this, World);

	    this.colliderPool = [];
	    this.characterPool = [];
	  }

	  _createClass(World, [{
	    key: "add",
	    value: function add(object) {
	      if (object.isOctree) {
	        this.colliderPool.push(object);
	      } else if (object.isCharacterController) {
	        this.characterPool.push(object);
	        object.world = this;
	      }
	    }
	  }, {
	    key: "step",
	    value: function step(dt) {
	      for (var i = 0, l = this.characterPool.length; i < l; i++) {
	        var character = this.characterPool[i];
	        var faces = void 0; // octree で絞られた node に含まれる face だけを
	        // character に渡して判定する

	        for (var ii = 0, ll = this.colliderPool.length; ii < ll; ii++) {
	          var octree = this.colliderPool[ii];
	          sphere.set(character.center, character.radius + character.groundPadding);
	          var intersectedNodes = octree.getIntersectedNodes(sphere, octree.maxDepth);
	          faces = Octree.uniqTrianglesFromNodes(intersectedNodes);
	        }

	        character.collisionCandidate = faces;
	        character.update(dt);
	      }
	    }
	  }]);

	  return World;
	}();

	// based on https://github.com/mrdoob/eventdispatcher.js/
	var EventDispatcher$1 = /*#__PURE__*/function () {
	  function EventDispatcher() {
	    _classCallCheck(this, EventDispatcher);

	    this._listeners = {};
	  }

	  _createClass(EventDispatcher, [{
	    key: "addEventListener",
	    value: function addEventListener(type, listener) {
	      var listeners = this._listeners;

	      if (listeners[type] === undefined) {
	        listeners[type] = [];
	      }

	      if (listeners[type].indexOf(listener) === -1) {
	        listeners[type].push(listener);
	      }
	    }
	  }, {
	    key: "hasEventListener",
	    value: function hasEventListener(type, listener) {
	      var listeners = this._listeners;
	      return listeners[type] !== undefined && listeners[type].indexOf(listener) !== -1;
	    }
	  }, {
	    key: "removeEventListener",
	    value: function removeEventListener(type, listener) {
	      var listeners = this._listeners;
	      var listenerArray = listeners[type];

	      if (listenerArray !== undefined) {
	        var index = listenerArray.indexOf(listener);

	        if (index !== -1) {
	          listenerArray.splice(index, 1);
	        }
	      }
	    }
	  }, {
	    key: "dispatchEvent",
	    value: function dispatchEvent(event) {
	      var listeners = this._listeners;
	      var listenerArray = listeners[event.type];

	      if (listenerArray !== undefined) {
	        event.target = this;
	        var array = listenerArray.slice(0);

	        for (var i = 0, l = array.length; i < l; i++) {
	          array[i].call(this, event);
	        }
	      }
	    }
	  }]);

	  return EventDispatcher;
	}();

	var PI_HALF$1 = Math.PI * 0.5;
	var PI_ONE_HALF = Math.PI * 1.5;
	var direction2D;
	var wallNormal2D;
	var groundingHead;
	var groundingTo;
	var point1;
	var point2;
	var direction;
	var translateScoped;
	var translate;
	onInstallHandlers.push(function () {
	  direction2D = new THREE$1.Vector2();
	  wallNormal2D = new THREE$1.Vector2();
	  groundingHead = new THREE$1.Vector3();
	  groundingTo = new THREE$1.Vector3();
	  point1 = new THREE$1.Vector3();
	  point2 = new THREE$1.Vector3();
	  direction = new THREE$1.Vector3();
	  translateScoped = new THREE$1.Vector3();
	  translate = new THREE$1.Vector3();
	});
	var CharacterController = /*#__PURE__*/function (_EventDispatcher) {
	  _inherits(CharacterController, _EventDispatcher);

	  var _super = _createSuper(CharacterController);

	  function CharacterController(object3d, radius, head, height) {
	    var _this;

	    _classCallCheck(this, CharacterController);

	    _this = _super.call(this);
	    _this.isCharacterController = true;
	    _this.object = object3d;
	    _this.headObject = head;
	    _this.height = height;
	    _this.fallVelocity = -20; // 下落速度

	    _this.jumpDuration = 1000; // 跳跃过程时间

	    _this.headHeight = 5;
	    _this.center = _this.object.position.clone();
	    _this.radius = radius;
	    _this.groundPadding = 0.5;
	    _this.maxSlopeGradient = Math.cos(50 * THREE$1.Math.DEG2RAD);
	    _this.isGrounded = false;
	    _this.isOnSlope = false;
	    _this.isIdling = false;
	    _this.isRunning = false;
	    _this.isWalking = false;
	    _this.isJumping = false;
	    _this.direction = 0; // 0 to 2PI(=360deg) in rad

	    _this.movementSpeed = 10; // Meters Per Second

	    _this.walkSpeed = 10; // Meters Per Second

	    _this.runSpeed = 30; // Meters Per Second

	    _this.moveAction = "run";
	    _this.velocity = new THREE$1.Vector3(0, -10, 0);
	    _this.currentJumpPower = 0;
	    _this.jumpStartTime = 0;
	    _this.groundHeight = 0;
	    _this.groundNormal = new THREE$1.Vector3();
	    _this.collisionCandidate;
	    _this.contactInfo = [];
	    var isFirstUpdate = true;
	    var wasGrounded;
	    var wasOnSlope; // let wasIdling;

	    var wasRunning;
	    var wasJumping;
	    var lastMoveAction;

	    _this._events = function () {
	      // 首次执行内容
	      if (isFirstUpdate) {
	        isFirstUpdate = false;
	        wasGrounded = _this.isGrounded;
	        wasOnSlope = _this.isOnSlope; // wasIdling   = this.isIdling;

	        wasRunning = _this.isRunning;
	        wasJumping = _this.isJumping;
	        return;
	      }

	      var moveName = _this.isWalking ? "walk" : "run";

	      if (!wasRunning && !_this.isRunning && _this.isGrounded && !_this.isIdling) {
	        _this.isIdling = true;

	        _this.dispatchEvent({
	          type: "startIdling"
	        });
	      } else if (!wasRunning && _this.isRunning && !_this.isJumping && _this.isGrounded || !wasGrounded && _this.isGrounded && _this.isRunning || wasOnSlope && !_this.isOnSlope && _this.isRunning && _this.isGrounded) {
	        _this.isIdling = false;

	        if (_this.isWalking) {
	          _this.dispatchEvent({
	            type: "startWalking"
	          });
	        } else {
	          _this.dispatchEvent({
	            type: "startRunning"
	          });
	        }
	      } else if (!wasJumping && _this.isJumping) {
	        _this.isIdling = false;

	        _this.dispatchEvent({
	          type: "startJumping"
	        });
	      } else if (!wasOnSlope && _this.isOnSlope) {
	        _this.dispatchEvent({
	          type: "startSliding"
	        });
	      } else if (wasGrounded && !_this.isGrounded && !_this.isJumping) {
	        _this.dispatchEvent({
	          type: "startFalling"
	        });
	      } else if (wasRunning && _this.isRunning && lastMoveAction !== moveName) {
	        lastMoveAction = moveName;

	        if (_this.isWalking) {
	          _this.dispatchEvent({
	            type: "startWalking"
	          });
	        } else {
	          _this.dispatchEvent({
	            type: "startRunning"
	          });
	        }
	      }

	      if (!wasGrounded && _this.isGrounded) ;

	      wasGrounded = _this.isGrounded;
	      wasOnSlope = _this.isOnSlope; // wasIdling   = this.isIdling;

	      wasRunning = _this.isRunning;
	      wasJumping = _this.isJumping;
	    };

	    return _this;
	  }

	  _createClass(CharacterController, [{
	    key: "update",
	    value: function update(dt) {
	      // 重置状态
	      this.isGrounded = false;
	      this.isOnSlope = false;
	      this.groundHeight = -Infinity;
	      this.groundNormal.set(0, 1, 0);

	      this._updateGrounding();

	      this._updateJumping();

	      this._updatePosition(dt);

	      this._collisionDetection();

	      this._solvePosition();

	      this._updateVelocity();

	      this._events();
	    }
	  }, {
	    key: "_updateVelocity",
	    value: function _updateVelocity() {
	      var frontDirection = -Math.cos(this.direction);
	      var rightDirection = -Math.sin(this.direction);
	      var isHittingCeiling = false;
	      var moveSpeed = this.isWalking ? this.walkSpeed : this.runSpeed;
	      this.velocity.set(rightDirection * moveSpeed * this.isRunning, this.fallVelocity, frontDirection * moveSpeed * this.isRunning); // 处理自动应用的速度,例如陡坡和自由落体

	      if (this.contactInfo.length === 0 && !this.isJumping) {
	        // 没有碰撞,所以自由落体
	        return;
	      } else if (this.isGrounded && !this.isOnSlope && !this.isJumping) {
	        // 如果你在正常的地面上,除了在跳跃开始时
	        this.velocity.y = 0;
	      } else if (this.isOnSlope) {
	        // TODO 0.2 是一个神奇的数字,所以想想如何从几何上找到它。
	        var slidingDownVelocity = this.fallVelocity;
	        var horizontalSpeed = -slidingDownVelocity / (1 - this.groundNormal.y) * 0.2;
	        this.velocity.x = this.groundNormal.x * horizontalSpeed;
	        this.velocity.y = this.fallVelocity;
	        this.velocity.z = this.groundNormal.z * horizontalSpeed;
	      } else if (!this.isGrounded && !this.isOnSlope && this.isJumping) {
	        // 跳跃处理
	        this.velocity.y = this.currentJumpPower * -this.fallVelocity;
	      } // 面向墙壁时将向墙壁的速度设置为0的处理
	      // vs 墙壁和在墙上滑动


	      direction2D.set(rightDirection, frontDirection); // const frontAngle = Math.atan2( direction2D.y, direction2D.x );

	      var negativeFrontAngle = Math.atan2(-direction2D.y, -direction2D.x);

	      for (var i = 0, l = this.contactInfo.length; i < l; i++) {
	        var normal = this.contactInfo[i].face.normal; // var distance = this.contactInfo[ i ].distance;

	        if (this.maxSlopeGradient < normal.y || this.isOnSlope) {
	          // 由于人脸是在地面上,所以没有像墙一样碰撞的可能性。
	          // 不衰减速度
	          continue;
	        }

	        if (!isHittingCeiling && normal.y < 0) {
	          isHittingCeiling = true;
	        }

	        wallNormal2D.set(normal.x, normal.z).normalize();
	        var wallAngle = Math.atan2(wallNormal2D.y, wallNormal2D.x);

	        if (Math.abs(negativeFrontAngle - wallAngle) >= PI_HALF$1 && //  90deg
	        Math.abs(negativeFrontAngle - wallAngle) <= PI_ONE_HALF // 270deg
	        ) {
	          // 面与行进方向相反,点为背面的墙
	          // 不衰减速度
	          continue;
	        } // 如果不满足以上条件,人脸就是墙
	        // 找到墙壁法线并将指向相反方向的速度向量设置为0


	        wallNormal2D.set(direction2D.dot(wallNormal2D) * wallNormal2D.x, direction2D.dot(wallNormal2D) * wallNormal2D.y);
	        direction2D.sub(wallNormal2D);

	        var _moveSpeed = this.isWalking ? this.walkSpeed : this.runSpeed;

	        this.velocity.x = direction2D.x * _moveSpeed * this.isRunning;
	        this.velocity.z = direction2D.y * _moveSpeed * this.isRunning;
	      } // 如果在跳跃时撞到天花板,中断跳跃


	      if (isHittingCeiling) {
	        this.velocity.y = Math.min(0, this.velocity.y);
	        this.isJumping = false;
	      }
	    }
	  }, {
	    key: "_updateGrounding",
	    value: function _updateGrounding() {
	      // “从头顶到几乎无限向下的分段 (segment)”与“三角形(triangle)”
	      // 进行交叉判断
	      // 如果与面部的交点在“头顶”和“地下填充(groundPadding)”之间
	      // isGrounded认定在地面上
	      //
	      //   ___
	      //  / | \
	      // |  |  | player sphere 玩家
	      //  \_|_/
	      //    |
	      //---[+]---- ground 地面
	      //    |
	      //    |
	      //    | segment (玩家头部到无限)
	      var groundContactInfo;
	      var groundContactInfoTmp;
	      var faces = this.collisionCandidate;
	      groundingHead.set(this.center.x, this.center.y + this.radius, this.center.z);
	      groundingTo.set(this.center.x, this.center.y - 1e10, this.center.z);

	      for (var i = 0, l = faces.length; i < l; i++) {
	        groundContactInfoTmp = testSegmentTriangle(groundingHead, groundingTo, faces[i].a, faces[i].b, faces[i].c);

	        if (groundContactInfoTmp && !groundContactInfo) {
	          groundContactInfo = groundContactInfoTmp;
	          groundContactInfo.face = faces[i];
	        } else if (groundContactInfoTmp && groundContactInfoTmp.contactPoint.y > groundContactInfo.contactPoint.y) {
	          groundContactInfo = groundContactInfoTmp;
	          groundContactInfo.face = faces[i];
	        }
	      }

	      if (!groundContactInfo) {
	        return;
	      }

	      this.groundHeight = groundContactInfo.contactPoint.y; // this.groundNormal.copy(groundContactInfo.face.normal);

	      var top = groundingHead.y;
	      var bottom = this.center.y - this.radius - this.groundPadding; // 跳跃和向上移动时不要强迫地面

	      if (this.isJumping && 0 < this.currentJumpPower) {
	        this.isOnSlope = false;
	        this.isGrounded = false;
	        return;
	      }

	      this.isGrounded = bottom <= this.groundHeight && this.groundHeight <= top;
	      this.isOnSlope = this.groundNormal.y <= this.maxSlopeGradient;

	      if (this.isGrounded) {
	        this.isJumping = false;
	      }
	    }
	  }, {
	    key: "_updatePosition",
	    value: function _updatePosition(dt) {
	      // 暂时忽略墙壁等(速度*时间)
	      // 推进中心坐标
	      // 与墙壁的碰撞检测会在下一步进行,这里就不做了
	      // 如果是isGrounded,强制将y的值调整到地面
	      var groundedY = this.groundHeight + this.radius;
	      var x = this.center.x + this.velocity.x * dt;
	      var y = this.center.y + this.velocity.y * dt;
	      var z = this.center.z + this.velocity.z * dt;
	      this.center.set(x, this.isGrounded ? groundedY : y, z);
	    }
	  }, {
	    key: "_collisionDetection",
	    value: function _collisionDetection() {
	      // 从可能相交的人脸列表中(collisionCandidate)
	      // 提取实际相交的墙面
	      // 添加到 this.contactInfo
	      var faces = this.collisionCandidate;
	      this.contactInfo.length = 0;

	      for (var i = 0, l = faces.length; i < l; i++) {
	        var contactInfo = isIntersectionSphereTriangle(this, faces[i].a, faces[i].b, faces[i].c, faces[i].normal);
	        if (!contactInfo) continue;
	        contactInfo.face = faces[i];
	        this.contactInfo.push(contactInfo);
	      }
	    }
	  }, {
	    key: "_calHeadPosition",
	    value: function _calHeadPosition(position) {
	      var center = {
	        x: position.x,
	        y: position.y + 1 + this.height,
	        z: position.z
	      };
	      return center;
	    }
	  }, {
	    key: "_solvePosition",
	    value: function _solvePosition() {
	      // 用 updatePosition() 运行中心后
	      // 如果它与墙壁碰撞并咬入它
	      // 这里从墙中挤出
	      var face;
	      var normal; // let distance;

	      if (this.contactInfo.length === 0) {
	        // 没有冲突
	        // 使用 center 的值退出
	        var _newPosition = {
	          x: this.center.x,
	          y: this.center.y - this.radius,
	          z: this.center.z
	        };
	        this.object.position.copy(_newPosition);
	        this.headObject.position.copy(this._calHeadPosition(_newPosition));
	        return;
	      } //
	      // vs 墙壁和在墙上滑动


	      translate.set(0, 0, 0);

	      for (var i = 0, l = this.contactInfo.length; i < l; i++) {
	        face = this.contactInfo[i].face;
	        normal = this.contactInfo[i].face.normal; // distance = this.contactInfo[ i ].distance;
	        // if ( 0 <= distance ) {
	        //   // 交差点までの距離が 0 以上ならこのフェイスとは衝突していない
	        //   // 無視する
	        //   continue;
	        // }

	        if (this.maxSlopeGradient < normal.y) {
	          // 这个三角形是地面或斜坡,而不是墙壁或天花板
	          // 面是非陡坡,即地面。
	          // 忽略接地过程,因为它在 updatePosition () 中解决
	          continue;
	        } // 面是否为陡坡


	        var isSlopeFace = this.maxSlopeGradient <= face.normal.y && face.normal.y < 1; // 如果您在跳跃下降过程中遇到陡坡,则跳跃结束

	        if (this.isJumping && 0 >= this.currentJumpPower && isSlopeFace) {
	          this.isJumping = false;
	          this.isGrounded = true; // console.log( 'jump end' );
	        }

	        if (this.isGrounded || this.isOnSlope) {
	          // 如果您在地面上,y(垂直)方向保持不变
	          // 通过仅更改 x、z(水平)方向进行拉伸
	          // http://gamedev.stackexchange.com/questions/80293/how-do-i-resolve-a-sphere-triangle-collision-in-a-given-direction
	          point1.copy(normal).multiplyScalar(-this.radius).add(this.center);
	          direction.set(normal.x, 0, normal.z).normalize();
	          var plainD = face.a.dot(normal);
	          var t = (plainD - (normal.x * point1.x + normal.y * point1.y + normal.z * point1.z)) / (normal.x * direction.x + normal.y * direction.y + normal.z * direction.z);
	          point2.copy(direction).multiplyScalar(t).add(point1);
	          translateScoped.subVectors(point2, point1);

	          if (Math.abs(translate.x) > Math.abs(translateScoped.x)) {
	            translate.x += translateScoped.x;
	          }

	          if (Math.abs(translate.z) > Math.abs(translateScoped.z)) {
	            translate.z += translateScoped.z;
	          } // break;


	          continue;
	        }
	      }

	      var newPosition = {
	        x: this.center.x,
	        y: this.center.y - this.radius,
	        z: this.center.z
	      };
	      this.object.position.copy(newPosition);
	      this.headObject.position.copy(this._calHeadPosition(newPosition));
	    }
	  }, {
	    key: "setDirection",
	    value: function setDirection() {}
	  }, {
	    key: "jump",
	    value: function jump() {
	      if (this.isJumping || !this.isGrounded || this.isOnSlope) return;
	      this.jumpStartTime = performance.now();
	      this.currentJumpPower = 1;
	      this.isJumping = true;
	    }
	  }, {
	    key: "_updateJumping",
	    value: function _updateJumping() {
	      if (!this.isJumping) return;
	      var elapsed = performance.now() - this.jumpStartTime;
	      var progress = elapsed / this.jumpDuration;
	      this.currentJumpPower = Math.cos(Math.min(progress, 1) * Math.PI);
	    }
	  }]);

	  return CharacterController;
	}(EventDispatcher$1);

	var TURN_DURATION = 200;
	var TAU = 2 * Math.PI;

	var modulo = function modulo(n, d) {
	  return (n % d + d) % d;
	};

	var getDeltaTurnAngle = function getDeltaTurnAngle(current, target) {
	  var a = modulo(current - target, TAU);
	  var b = modulo(target - current, TAU);
	  return a < b ? -a : b;
	};

	var AnimationController = /*#__PURE__*/function () {
	  function AnimationController(mesh) {
	    _classCallCheck(this, AnimationController);

	    this.mesh = mesh;
	    this.motion = {};
	    this.mixer = new THREE$1.AnimationMixer(mesh.scene);
	    this.currentMotionName = "";

	    for (var i = 0, l = this.mesh.animations.length; i < l; i++) {
	      var anim = this.mesh.animations[i];
	      this.motion[anim.name] = this.mixer.clipAction(anim);
	      this.motion[anim.name].setEffectiveWeight(1);
	    }
	  }

	  _createClass(AnimationController, [{
	    key: "play",
	    value: function play(name) {
	      if (this.currentMotionName === name) return;

	      if (this.motion[this.currentMotionName]) {
	        var from = this.motion[this.currentMotionName].play();
	        var to = this.motion[name].play();
	        from.enabled = true;
	        to.enabled = true;
	        from.crossFadeTo(to, 0.3);
	      } else {
	        this.motion[name].enabled = true;
	        this.motion[name].play();
	      }

	      this.currentMotionName = name;
	    }
	  }, {
	    key: "turn",
	    value: function turn(rad, immediate) {
	      var that = this;
	      var prevRotY = this.mesh.rotation.y;
	      var targetRotY = rad;
	      var deltaY = getDeltaTurnAngle(prevRotY, targetRotY); // const duration   = Math.abs( deltaY ) * 100;

	      var start = Date.now();
	      var end = start + TURN_DURATION;
	      var progress = 0;

	      if (immediate) {
	        this.mesh.rotation.y = targetRotY;
	        return;
	      }

	      if (this._targetRotY === targetRotY) return;
	      this._targetRotY = targetRotY;
	      {
	        var _targetRotY = targetRotY;

	        (function interval() {
	          var now = Date.now();
	          var isAborted = _targetRotY !== that._targetRotY;
	          if (isAborted) return;

	          if (now >= end) {
	            that.mesh.rotation.y = _targetRotY;
	            delete that._targetRotY;
	            return;
	          }

	          requestAnimationFrame(interval);
	          progress = (now - start) / TURN_DURATION;
	          that.mesh.rotation.y = prevRotY + deltaY * progress;
	        })();
	      }
	    }
	  }, {
	    key: "update",
	    value: function update(delta) {
	      this.mixer.update(delta);
	    }
	  }]);

	  return AnimationController;
	}();

	var KEY_W = 87;
	var KEY_UP = 38;
	var KEY_S = 83;
	var KEY_DOWN = 40;
	var KEY_A = 65;
	var KEY_LEFT = 37;
	var KEY_D = 68;
	var KEY_RIGHT = 39;
	var KEY_SPACE = 32;
	var DEG2RAD = Math.PI / 180;
	var DEG_0 = 0 * DEG2RAD;
	var DEG_45 = 45 * DEG2RAD;
	var DEG_90 = 90 * DEG2RAD;
	var DEG_135 = 135 * DEG2RAD;
	var DEG_180 = 180 * DEG2RAD;
	var DEG_225 = 225 * DEG2RAD;
	var DEG_270 = 270 * DEG2RAD;
	var DEG_315 = 315 * DEG2RAD;
	var KeyInputControl = /*#__PURE__*/function (_EventDispatcher) {
	  _inherits(KeyInputControl, _EventDispatcher);

	  var _super = _createSuper(KeyInputControl);

	  function KeyInputControl() {
	    var _this;

	    _classCallCheck(this, KeyInputControl);

	    _this = _super.call(this);
	    _this.isDisabled = false;
	    _this.isUp = false;
	    _this.isDown = false;
	    _this.isLeft = false;
	    _this.isRight = false;
	    _this.isMoveKeyHolding = false;
	    _this.frontAngle = 0;
	    _this._keydownListener = onKeyDown.bind(_assertThisInitialized(_this));
	    _this._keyupListener = onKeyUp.bind(_assertThisInitialized(_this));
	    _this._blurListener = onBlur.bind(_assertThisInitialized(_this));
	    window.addEventListener('keydown', _this._keydownListener);
	    window.addEventListener('keyup', _this._keyupListener);
	    window.addEventListener('blur', _this._blurListener);
	    return _this;
	  }

	  _createClass(KeyInputControl, [{
	    key: "jump",
	    value: function jump() {
	      this.dispatchEvent({
	        type: 'jumpkeypress'
	      });
	    }
	  }, {
	    key: "updateAngle",
	    value: function updateAngle() {
	      var up = this.isUp;
	      var down = this.isDown;
	      var left = this.isLeft;
	      var right = this.isRight;
	      if (up && !left && !down && !right) this.frontAngle = DEG_0;else if (up && left && !down && !right) this.frontAngle = DEG_45;else if (!up && left && !down && !right) this.frontAngle = DEG_90;else if (!up && left && down && !right) this.frontAngle = DEG_135;else if (!up && !left && down && !right) this.frontAngle = DEG_180;else if (!up && !left && down && right) this.frontAngle = DEG_225;else if (!up && !left && !down && right) this.frontAngle = DEG_270;else if (up && !left && !down && right) this.frontAngle = DEG_315;
	    }
	  }]);

	  return KeyInputControl;
	}(EventDispatcher$1);

	function onKeyDown(event) {
	  if (this.isDisabled) return;
	  if (isInputEvent(event)) return;

	  switch (event.keyCode) {
	    case KEY_W:
	    case KEY_UP:
	      this.isUp = true;
	      break;

	    case KEY_S:
	    case KEY_DOWN:
	      this.isDown = true;
	      break;

	    case KEY_A:
	    case KEY_LEFT:
	      this.isLeft = true;
	      break;

	    case KEY_D:
	    case KEY_RIGHT:
	      this.isRight = true;
	      break;

	    case KEY_SPACE:
	      this.jump();
	      break;

	    default:
	      return;
	  }

	  var prevAngle = this.frontAngle;
	  this.updateAngle();

	  if (prevAngle !== this.frontAngle) {
	    this.dispatchEvent({
	      type: 'movekeychange'
	    });
	  }

	  if ((this.isUp || this.isDown || this.isLeft || this.isRight) && !this.isMoveKeyHolding) {
	    this.isMoveKeyHolding = true;
	    this.dispatchEvent({
	      type: 'movekeyon'
	    });
	  }
	}

	function onKeyUp(event) {
	  if (this.isDisabled) return;

	  switch (event.keyCode) {
	    case KEY_W:
	    case KEY_UP:
	      this.isUp = false;
	      break;

	    case KEY_S:
	    case KEY_DOWN:
	      this.isDown = false;
	      break;

	    case KEY_A:
	    case KEY_LEFT:
	      this.isLeft = false;
	      break;

	    case KEY_D:
	    case KEY_RIGHT:
	      this.isRight = false;
	      break;

	    case KEY_SPACE:
	      break;

	    default:
	      return;
	  }

	  var prevAngle = this.frontAngle;
	  this.updateAngle();

	  if (prevAngle !== this.frontAngle) {
	    this.dispatchEvent({
	      type: 'movekeychange'
	    });
	  }

	  if (!this.isUp && !this.isDown && !this.isLeft && !this.isRight && (event.keyCode === KEY_W || event.keyCode === KEY_UP || event.keyCode === KEY_S || event.keyCode === KEY_DOWN || event.keyCode === KEY_A || event.keyCode === KEY_LEFT || event.keyCode === KEY_D || event.keyCode === KEY_RIGHT)) {
	    this.isMoveKeyHolding = false;
	    this.dispatchEvent({
	      type: 'movekeyoff'
	    });
	  }
	}

	function onBlur() {
	  this.isUp = false;
	  this.isDown = false;
	  this.isLeft = false;
	  this.isRight = false;

	  if (this.isMoveKeyHolding) {
	    this.isMoveKeyHolding = false;
	    this.dispatchEvent({
	      type: 'movekeyoff'
	    });
	  }
	}

	function isInputEvent(event) {
	  var target = event.target;
	  return target.tagName === 'INPUT' || target.tagName === 'SELECT' || target.tagName === 'TEXTAREA' || target.isContentEditable;
	}

	/*!
	 * ggabcd-camera-controls
	 * https://github.com/yomotsu/camera-controls
	 * (c) 2017 @yomotsu
	 * Released under the MIT License.
	 */
	var ACTION;
	(function (ACTION) {
	    ACTION[ACTION["NONE"] = 0] = "NONE";
	    ACTION[ACTION["ROTATE"] = 1] = "ROTATE";
	    ACTION[ACTION["TRUCK"] = 2] = "TRUCK";
	    ACTION[ACTION["OFFSET"] = 3] = "OFFSET";
	    ACTION[ACTION["DOLLY"] = 4] = "DOLLY";
	    ACTION[ACTION["ZOOM"] = 5] = "ZOOM";
	    ACTION[ACTION["TOUCH_ROTATE"] = 6] = "TOUCH_ROTATE";
	    ACTION[ACTION["TOUCH_TRUCK"] = 7] = "TOUCH_TRUCK";
	    ACTION[ACTION["TOUCH_OFFSET"] = 8] = "TOUCH_OFFSET";
	    ACTION[ACTION["TOUCH_DOLLY"] = 9] = "TOUCH_DOLLY";
	    ACTION[ACTION["TOUCH_ZOOM"] = 10] = "TOUCH_ZOOM";
	    ACTION[ACTION["TOUCH_DOLLY_TRUCK"] = 11] = "TOUCH_DOLLY_TRUCK";
	    ACTION[ACTION["TOUCH_DOLLY_OFFSET"] = 12] = "TOUCH_DOLLY_OFFSET";
	    ACTION[ACTION["TOUCH_ZOOM_TRUCK"] = 13] = "TOUCH_ZOOM_TRUCK";
	    ACTION[ACTION["TOUCH_ZOOM_OFFSET"] = 14] = "TOUCH_ZOOM_OFFSET";
	})(ACTION || (ACTION = {}));
	function isPerspectiveCamera(camera) {
	    return camera.isPerspectiveCamera;
	}
	function isOrthographicCamera(camera) {
	    return camera.isOrthographicCamera;
	}

	const PI_2 = Math.PI * 2;
	const PI_HALF = Math.PI / 2;

	const EPSILON = 1e-5;
	function approxZero(number, error = EPSILON) {
	    return Math.abs(number) < error;
	}
	function approxEquals(a, b, error = EPSILON) {
	    return approxZero(a - b, error);
	}
	function roundToStep(value, step) {
	    return Math.round(value / step) * step;
	}
	function infinityToMaxNumber(value) {
	    if (isFinite(value))
	        return value;
	    if (value < 0)
	        return -Number.MAX_VALUE;
	    return Number.MAX_VALUE;
	}
	function maxNumberToInfinity(value) {
	    if (Math.abs(value) < Number.MAX_VALUE)
	        return value;
	    return value * Infinity;
	}

	function extractClientCoordFromEvent(pointers, out) {
	    out.set(0, 0);
	    pointers.forEach((pointer) => {
	        out.x += pointer.clientX;
	        out.y += pointer.clientY;
	    });
	    out.x /= pointers.length;
	    out.y /= pointers.length;
	}

	function notSupportedInOrthographicCamera(camera, message) {
	    if (isOrthographicCamera(camera)) {
	        console.warn(`${message} is not supported in OrthographicCamera`);
	        return true;
	    }
	    return false;
	}

	function quatInvertCompat(target) {
	    if (target.invert) {
	        target.invert();
	    }
	    else {
	        target.inverse();
	    }
	    return target;
	}

	class EventDispatcher {
	    constructor() {
	        this._listeners = {};
	    }
	    addEventListener(type, listener) {
	        const listeners = this._listeners;
	        if (listeners[type] === undefined)
	            listeners[type] = [];
	        if (listeners[type].indexOf(listener) === -1)
	            listeners[type].push(listener);
	    }
	    removeEventListener(type, listener) {
	        const listeners = this._listeners;
	        const listenerArray = listeners[type];
	        if (listenerArray !== undefined) {
	            const index = listenerArray.indexOf(listener);
	            if (index !== -1)
	                listenerArray.splice(index, 1);
	        }
	    }
	    removeAllEventListeners(type) {
	        if (!type) {
	            this._listeners = {};
	            return;
	        }
	        if (Array.isArray(this._listeners[type]))
	            this._listeners[type].length = 0;
	    }
	    dispatchEvent(event) {
	        const listeners = this._listeners;
	        const listenerArray = listeners[event.type];
	        if (listenerArray !== undefined) {
	            event.target = this;
	            const array = listenerArray.slice(0);
	            for (let i = 0, l = array.length; i < l; i++) {
	                array[i].call(this, event);
	            }
	        }
	    }
	}

	const isBrowser = typeof window !== 'undefined';
	const isMac = isBrowser && /Mac/.test(navigator.platform);
	const isPointerEventsNotSupported = !(isBrowser && 'PointerEvent' in window);
	const readonlyACTION = Object.freeze(ACTION);
	const TOUCH_DOLLY_FACTOR = 1 / 8;
	let THREE;
	let _ORIGIN;
	let _AXIS_Y;
	let _AXIS_Z;
	let _v2;
	let _v3A;
	let _v3B;
	let _v3C;
	let _xColumn;
	let _yColumn;
	let _zColumn;
	let _sphericalA;
	let _sphericalB;
	let _box3A;
	let _box3B;
	let _sphere;
	let _quaternionA;
	let _quaternionB;
	let _rotationMatrix;
	let _raycaster;
	class CameraControls extends EventDispatcher {
	    constructor(camera, domElement) {
	        super();
	        this.minPolarAngle = 0;
	        this.maxPolarAngle = Math.PI;
	        this.minAzimuthAngle = -Infinity;
	        this.maxAzimuthAngle = Infinity;
	        this.minDistance = 0;
	        this.maxDistance = Infinity;
	        this.infinityDolly = false;
	        this.minZoom = 0.01;
	        this.maxZoom = Infinity;
	        this.dampingFactor = 0.05;
	        this.draggingDampingFactor = 0.25;
	        this.azimuthRotateSpeed = 1.0;
	        this.polarRotateSpeed = 1.0;
	        this.dollySpeed = 1.0;
	        this.truckSpeed = 2.0;
	        this.dragSpeed = 1;
	        this.dollyToCursor = false;
	        this.dragToOffset = false;
	        this.verticalDragToForward = false;
	        this.boundaryFriction = 0.0;
	        this.restThreshold = 0.01;
	        this.colliderMeshes = [];
	        this.cancel = () => { };
	        this._enabled = true;
	        this._state = ACTION.NONE;
	        this._viewport = null;
	        this._dollyControlAmount = 0;
	        this._hasRested = true;
	        this._boundaryEnclosesCamera = false;
	        this._needsUpdate = true;
	        this._updatedLastTime = false;
	        this._elementRect = new DOMRect();
	        this._activePointers = [];
	        this._truckInternal = (deltaX, deltaY, dragToOffset) => {
	            if (isPerspectiveCamera(this._camera)) {
	                const offset = _v3A.copy(this._camera.position).sub(this._target);
	                const fov = this._camera.getEffectiveFOV() * THREE.MathUtils.DEG2RAD;
	                const targetDistance = offset.length() * Math.tan(fov * 0.5);
	                const truckX = (this.truckSpeed * deltaX * targetDistance / this._elementRect.height);
	                const pedestalY = (this.truckSpeed * deltaY * targetDistance / this._elementRect.height);
	                if (this.verticalDragToForward) {
	                    dragToOffset ?
	                        this.setFocalOffset(this._focalOffsetEnd.x + truckX, this._focalOffsetEnd.y, this._focalOffsetEnd.z, true) :
	                        this.truck(truckX, 0, true);
	                    this.forward(-pedestalY, true);
	                }
	                else {
	                    dragToOffset ?
	                        this.setFocalOffset(this._focalOffsetEnd.x + truckX, this._focalOffsetEnd.y + pedestalY, this._focalOffsetEnd.z, true) :
	                        this.truck(truckX, pedestalY, true);
	                }
	            }
	            else if (isOrthographicCamera(this._camera)) {
	                const camera = this._camera;
	                const truckX = deltaX * (camera.right - camera.left) / camera.zoom / this._elementRect.width;
	                const pedestalY = deltaY * (camera.top - camera.bottom) / camera.zoom / this._elementRect.height;
	                dragToOffset ?
	                    this.setFocalOffset(this._focalOffsetEnd.x + truckX, this._focalOffsetEnd.y + pedestalY, this._focalOffsetEnd.z, true) :
	                    this.truck(truckX, pedestalY, true);
	            }
	        };
	        this._rotateInternal = (deltaX, deltaY) => {
	            const theta = PI_2 * this.azimuthRotateSpeed * deltaX / this._elementRect.height;
	            const phi = PI_2 * this.polarRotateSpeed * deltaY / this._elementRect.height;
	            this.rotate(theta, phi, true);
	        };
	        this._dollyInternal = (delta, x, y) => {
	            const dollyScale = Math.pow(0.95, -delta * this.dollySpeed);
	            const distance = this._sphericalEnd.radius * dollyScale;
	            const prevRadius = this._sphericalEnd.radius;
	            const signedPrevRadius = prevRadius * (delta >= 0 ? -1 : 1);
	            this.dollyTo(distance);
	            if (this.infinityDolly && (distance < this.minDistance || this.maxDistance === this.minDistance)) {
	                this._camera.getWorldDirection(_v3A);
	                this._targetEnd.add(_v3A.normalize().multiplyScalar(signedPrevRadius));
	                this._target.add(_v3A.normalize().multiplyScalar(signedPrevRadius));
	            }
	            if (this.dollyToCursor) {
	                this._dollyControlAmount += this._sphericalEnd.radius - prevRadius;
	                if (this.infinityDolly && (distance < this.minDistance || this.maxDistance === this.minDistance)) {
	                    this._dollyControlAmount -= signedPrevRadius;
	                }
	                this._dollyControlCoord.set(x, y);
	            }
	            return;
	        };
	        this._zoomInternal = (delta, x, y) => {
	            const zoomScale = Math.pow(0.95, delta * this.dollySpeed);
	            this.zoomTo(this._zoom * zoomScale);
	            if (this.dollyToCursor) {
	                this._dollyControlAmount = this._zoomEnd;
	                this._dollyControlCoord.set(x, y);
	            }
	            return;
	        };
	        if (typeof THREE === 'undefined') {
	            console.error('camera-controls: `THREE` is undefined. You must first run `CameraControls.install( { THREE: THREE } )`. Check the docs for further information.');
	        }
	        this._camera = camera;
	        this._yAxisUpSpace = new THREE.Quaternion().setFromUnitVectors(this._camera.up, _AXIS_Y);
	        this._yAxisUpSpaceInverse = quatInvertCompat(this._yAxisUpSpace.clone());
	        this._state = ACTION.NONE;
	        this._domElement = domElement;
	        this._domElement.style.touchAction = 'none';
	        this._domElement.style.userSelect = 'none';
	        this._domElement.style.webkitUserSelect = 'none';
	        this._target = new THREE.Vector3();
	        this._targetEnd = this._target.clone();
	        this._focalOffset = new THREE.Vector3();
	        this._focalOffsetEnd = this._focalOffset.clone();
	        this._spherical = new THREE.Spherical().setFromVector3(_v3A.copy(this._camera.position).applyQuaternion(this._yAxisUpSpace));
	        this._sphericalEnd = this._spherical.clone();
	        this._zoom = this._camera.zoom;
	        this._zoomEnd = this._zoom;
	        this._nearPlaneCorners = [
	            new THREE.Vector3(),
	            new THREE.Vector3(),
	            new THREE.Vector3(),
	            new THREE.Vector3(),
	        ];
	        this._updateNearPlaneCorners();
	        this._boundary = new THREE.Box3(new THREE.Vector3(-Infinity, -Infinity, -Infinity), new THREE.Vector3(Infinity, Infinity, Infinity));
	        this._target0 = this._target.clone();
	        this._position0 = this._camera.position.clone();
	        this._zoom0 = this._zoom;
	        this._focalOffset0 = this._focalOffset.clone();
	        this._dollyControlAmount = 0;
	        this._dollyControlCoord = new THREE.Vector2();
	        this.mouseButtons = {
	            left: ACTION.ROTATE,
	            middle: ACTION.DOLLY,
	            right: ACTION.TRUCK,
	            wheel: isPerspectiveCamera(this._camera) ? ACTION.DOLLY :
	                isOrthographicCamera(this._camera) ? ACTION.ZOOM :
	                    ACTION.NONE,
	            shiftLeft: ACTION.NONE,
	        };
	        this.touches = {
	            one: ACTION.TOUCH_ROTATE,
	            two: isPerspectiveCamera(this._camera) ? ACTION.TOUCH_DOLLY_TRUCK :
	                isOrthographicCamera(this._camera) ? ACTION.TOUCH_ZOOM_TRUCK :
	                    ACTION.NONE,
	            three: ACTION.TOUCH_TRUCK,
	        };
	        if (this._domElement) {
	            const dragStartPosition = new THREE.Vector2();
	            const lastDragPosition = new THREE.Vector2();
	            const dollyStart = new THREE.Vector2();
	            const onPointerDown = (event) => {
	                if (!this._enabled)
	                    return;
	                const pointer = {
	                    pointerId: event.pointerId,
	                    clientX: event.clientX,
	                    clientY: event.clientY,
	                };
	                this._activePointers.push(pointer);
	                switch (event.button) {
	                    case THREE.MOUSE.LEFT:
	                        this._state = event.shiftKey ? this.mouseButtons.shiftLeft : this.mouseButtons.left;
	                        break;
	                    case THREE.MOUSE.MIDDLE:
	                        this._state = this.mouseButtons.middle;
	                        break;
	                    case THREE.MOUSE.RIGHT:
	                        this._state = this.mouseButtons.right;
	                        break;
	                }
	                if (event.pointerType === 'touch') {
	                    switch (this._activePointers.length) {
	                        case 1:
	                            this._state = this.touches.one;
	                            break;
	                        case 2:
	                            this._state = this.touches.two;
	                            break;
	                        case 3:
	                            this._state = this.touches.three;
	                            break;
	                    }
	                }
	                this._domElement.ownerDocument.removeEventListener('pointermove', onPointerMove, { passive: false });
	                this._domElement.ownerDocument.removeEventListener('pointerup', onPointerUp);
	                this._domElement.ownerDocument.addEventListener('pointermove', onPointerMove, { passive: false });
	                this._domElement.ownerDocument.addEventListener('pointerup', onPointerUp);
	                startDragging();
	            };
	            const onMouseDown = (event) => {
	                if (!this._enabled)
	                    return;
	                const pointer = {
	                    pointerId: 0,
	                    clientX: event.clientX,
	                    clientY: event.clientY,
	                };
	                this._activePointers.push(pointer);
	                switch (event.button) {
	                    case THREE.MOUSE.LEFT:
	                        this._state = event.shiftKey ? this.mouseButtons.shiftLeft : this.mouseButtons.left;
	                        break;
	                    case THREE.MOUSE.MIDDLE:
	                        this._state = this.mouseButtons.middle;
	                        break;
	                    case THREE.MOUSE.RIGHT:
	                        this._state = this.mouseButtons.right;
	                        break;
	                }
	                this._domElement.ownerDocument.removeEventListener('mousemove', onMouseMove);
	                this._domElement.ownerDocument.removeEventListener('mouseup', onMouseUp);
	                this._domElement.ownerDocument.addEventListener('mousemove', onMouseMove);
	                this._domElement.ownerDocument.addEventListener('mouseup', onMouseUp);
	                startDragging();
	            };
	            const onTouchStart = (event) => {
	                console.log('drag');
	                if (!this._enabled)
	                    return;
	                event.preventDefault();
	                Array.prototype.forEach.call(event.changedTouches, (touch) => {
	                    const pointer = {
	                        pointerId: touch.identifier,
	                        clientX: touch.clientX,
	                        clientY: touch.clientY,
	                    };
	                    this._activePointers.push(pointer);
	                });
	                switch (this._activePointers.length) {
	                    case 1:
	                        this._state = this.touches.one;
	                        break;
	                    case 2:
	                        this._state = this.touches.two;
	                        break;
	                    case 3:
	                        this._state = this.touches.three;
	                        break;
	                }
	                this._domElement.ownerDocument.removeEventListener('touchmove', onTouchMove, { passive: false });
	                this._domElement.ownerDocument.removeEventListener('touchend', onTouchEnd);
	                this._domElement.ownerDocument.addEventListener('touchmove', onTouchMove, { passive: false });
	                this._domElement.ownerDocument.addEventListener('touchend', onTouchEnd);
	                startDragging();
	            };
	            const onPointerMove = (event) => {
	                if (event.cancelable)
	                    event.preventDefault();
	                const pointerId = event.pointerId;
	                const pointer = this._findPointerById(pointerId);
	                if (!pointer)
	                    return;
	                pointer.clientX = event.clientX;
	                pointer.clientY = event.clientY;
	                dragging();
	            };
	            const onMouseMove = (event) => {
	                const pointer = this._findPointerById(0);
	                if (!pointer)
	                    return;
	                pointer.clientX = event.clientX;
	                pointer.clientY = event.clientY;
	                dragging();
	            };
	            const onTouchMove = (event) => {
	                if (event.cancelable)
	                    event.preventDefault();
	                Array.prototype.forEach.call(event.changedTouches, (touch) => {
	                    const pointerId = touch.identifier;
	                    const pointer = this._findPointerById(pointerId);
	                    if (!pointer)
	                        return;
	                    pointer.clientX = touch.clientX;
	                    pointer.clientY = touch.clientY;
	                });
	                dragging();
	            };
	            const onPointerUp = (event) => {
	                const pointerId = event.pointerId;
	                const pointer = this._findPointerById(pointerId);
	                pointer && this._activePointers.splice(this._activePointers.indexOf(pointer), 1);
	                if (event.pointerType === 'touch') {
	                    switch (this._activePointers.length) {
	                        case 0:
	                            this._state = ACTION.NONE;
	                            break;
	                        case 1:
	                            this._state = this.touches.one;
	                            break;
	                        case 2:
	                            this._state = this.touches.two;
	                            break;
	                        case 3:
	                            this._state = this.touches.three;
	                            break;
	                    }
	                }
	                else {
	                    this._state = ACTION.NONE;
	                }
	                endDragging();
	            };
	            const onMouseUp = () => {
	                const pointer = this._findPointerById(0);
	                pointer && this._activePointers.splice(this._activePointers.indexOf(pointer), 1);
	                this._state = ACTION.NONE;
	                endDragging();
	            };
	            const onTouchEnd = (event) => {
	                Array.prototype.forEach.call(event.changedTouches, (touch) => {
	                    const pointerId = touch.identifier;
	                    const pointer = this._findPointerById(pointerId);
	                    pointer && this._activePointers.splice(this._activePointers.indexOf(pointer), 1);
	                });
	                switch (this._activePointers.length) {
	                    case 0:
	                        this._state = ACTION.NONE;
	                        break;
	                    case 1:
	                        this._state = this.touches.one;
	                        break;
	                    case 2:
	                        this._state = this.touches.two;
	                        break;
	                    case 3:
	                        this._state = this.touches.three;
	                        break;
	                }
	                endDragging();
	            };
	            let lastScrollTimeStamp = -1;
	            const onMouseWheel = (event) => {
	                if (!this._enabled || this.mouseButtons.wheel === ACTION.NONE)
	                    return;
	                event.preventDefault();
	                if (this.dollyToCursor ||
	                    this.mouseButtons.wheel === ACTION.ROTATE ||
	                    this.mouseButtons.wheel === ACTION.TRUCK) {
	                    const now = performance.now();
	                    if (lastScrollTimeStamp - now < 1000)
	                        this._getClientRect(this._elementRect);
	                    lastScrollTimeStamp = now;
	                }
	                const deltaYFactor = isMac ? -1 : -3;
	                const delta = (event.deltaMode === 1) ? event.deltaY / deltaYFactor : event.deltaY / (deltaYFactor * 10);
	                const x = this.dollyToCursor ? (event.clientX - this._elementRect.x) / this._elementRect.width * 2 - 1 : 0;
	                const y = this.dollyToCursor ? (event.clientY - this._elementRect.y) / this._elementRect.height * -2 + 1 : 0;
	                switch (this.mouseButtons.wheel) {
	                    case ACTION.ROTATE: {
	                        this._rotateInternal(event.deltaX, event.deltaY);
	                        break;
	                    }
	                    case ACTION.TRUCK: {
	                        this._truckInternal(event.deltaX, event.deltaY, false);
	                        break;
	                    }
	                    case ACTION.OFFSET: {
	                        this._truckInternal(event.deltaX, event.deltaY, true);
	                        break;
	                    }
	                    case ACTION.DOLLY: {
	                        this._dollyInternal(-delta, x, y);
	                        break;
	                    }
	                    case ACTION.ZOOM: {
	                        this._zoomInternal(-delta, x, y);
	                        break;
	                    }
	                }
	                this.dispatchEvent({ type: 'control' });
	            };
	            const onContextMenu = (event) => {
	                if (!this._enabled)
	                    return;
	                event.preventDefault();
	            };
	            const startDragging = () => {
	                if (!this._enabled)
	                    return;
	                extractClientCoordFromEvent(this._activePointers, _v2);
	                this._getClientRect(this._elementRect);
	                dragStartPosition.copy(_v2);
	                lastDragPosition.copy(_v2);
	                const isMultiTouch = this._activePointers.length >= 2;
	                if (isMultiTouch) {
	                    const dx = _v2.x - this._activePointers[1].clientX;
	                    const dy = _v2.y - this._activePointers[1].clientY;
	                    const distance = Math.sqrt(dx * dx + dy * dy);
	                    dollyStart.set(0, distance);
	                    const x = (this._activePointers[0].clientX + this._activePointers[1].clientX) * 0.5;
	                    const y = (this._activePointers[0].clientY + this._activePointers[1].clientY) * 0.5;
	                    lastDragPosition.set(x, y);
	                }
	                this.dispatchEvent({ type: 'controlstart' });
	            };
	            const dragging = () => {
	                if (!this._enabled)
	                    return;
	                extractClientCoordFromEvent(this._activePointers, _v2);
	                const deltaX = lastDragPosition.x - _v2.x;
	                const deltaY = lastDragPosition.y - _v2.y;
	                lastDragPosition.copy(_v2);
	                switch (this._state) {
	                    case ACTION.ROTATE:
	                    case ACTION.TOUCH_ROTATE: {
	                        this._rotateInternal(deltaX * this.dragSpeed, deltaY * this.dragSpeed);
	                        break;
	                    }
	                    case ACTION.DOLLY:
	                    case ACTION.ZOOM: {
	                        const dollyX = this.dollyToCursor ? (dragStartPosition.x - this._elementRect.x) / this._elementRect.width * 2 - 1 : 0;
	                        const dollyY = this.dollyToCursor ? (dragStartPosition.y - this._elementRect.y) / this._elementRect.height * -2 + 1 : 0;
	                        this._state === ACTION.DOLLY ?
	                            this._dollyInternal(deltaY * TOUCH_DOLLY_FACTOR, dollyX, dollyY) :
	                            this._zoomInternal(deltaY * TOUCH_DOLLY_FACTOR, dollyX, dollyY);
	                        break;
	                    }
	                    case ACTION.TOUCH_DOLLY:
	                    case ACTION.TOUCH_ZOOM:
	                    case ACTION.TOUCH_DOLLY_TRUCK:
	                    case ACTION.TOUCH_ZOOM_TRUCK:
	                    case ACTION.TOUCH_DOLLY_OFFSET:
	                    case ACTION.TOUCH_ZOOM_OFFSET: {
	                        const dx = _v2.x - this._activePointers[1].clientX;
	                        const dy = _v2.y - this._activePointers[1].clientY;
	                        const distance = Math.sqrt(dx * dx + dy * dy);
	                        const dollyDelta = dollyStart.y - distance;
	                        dollyStart.set(0, distance);
	                        const dollyX = this.dollyToCursor ? (lastDragPosition.x - this._elementRect.x) / this._elementRect.width * 2 - 1 : 0;
	                        const dollyY = this.dollyToCursor ? (lastDragPosition.y - this._elementRect.y) / this._elementRect.height * -2 + 1 : 0;
	                        this._state === ACTION.TOUCH_DOLLY ||
	                            this._state === ACTION.TOUCH_DOLLY_TRUCK ||
	                            this._state === ACTION.TOUCH_DOLLY_OFFSET ?
	                            this._dollyInternal(dollyDelta * TOUCH_DOLLY_FACTOR, dollyX, dollyY) :
	                            this._zoomInternal(dollyDelta * TOUCH_DOLLY_FACTOR, dollyX, dollyY);
	                        if (this._state === ACTION.TOUCH_DOLLY_TRUCK ||
	                            this._state === ACTION.TOUCH_ZOOM_TRUCK) {
	                            this._truckInternal(deltaX, deltaY, false);
	                        }
	                        else if (this._state === ACTION.TOUCH_DOLLY_OFFSET ||
	                            this._state === ACTION.TOUCH_ZOOM_OFFSET) {
	                            this._truckInternal(deltaX, deltaY, true);
	                        }
	                        break;
	                    }
	                    case ACTION.TRUCK:
	                    case ACTION.TOUCH_TRUCK: {
	                        this._truckInternal(deltaX, deltaY, false);
	                        break;
	                    }
	                    case ACTION.OFFSET:
	                    case ACTION.TOUCH_OFFSET: {
	                        this._truckInternal(deltaX, deltaY, true);
	                        break;
	                    }
	                }
	                this.dispatchEvent({ type: 'control' });
	            };
	            const endDragging = () => {
	                extractClientCoordFromEvent(this._activePointers, _v2);
	                lastDragPosition.copy(_v2);
	                if (this._activePointers.length === 0) {
	                    this._domElement.ownerDocument.removeEventListener('pointermove', onPointerMove, { passive: false });
	                    this._domElement.ownerDocument.removeEventListener('pointerup', onPointerUp);
	                    this._domElement.ownerDocument.removeEventListener('touchmove', onTouchMove, { passive: false });
	                    this._domElement.ownerDocument.removeEventListener('touchend', onTouchEnd);
	                    this.dispatchEvent({ type: 'controlend' });
	                }
	            };
	            this._domElement.addEventListener('pointerdown', onPointerDown);
	            isPointerEventsNotSupported && this._domElement.addEventListener('mousedown', onMouseDown);
	            isPointerEventsNotSupported && this._domElement.addEventListener('touchstart', onTouchStart);
	            this._domElement.addEventListener('pointercancel', onPointerUp);
	            this._domElement.addEventListener('wheel', onMouseWheel, { passive: false });
	            this._domElement.addEventListener('contextmenu', onContextMenu);
	            this._removeAllEventListeners = () => {
	                this._domElement.removeEventListener('pointerdown', onPointerDown);
	                this._domElement.removeEventListener('mousedown', onMouseDown);
	                this._domElement.removeEventListener('touchstart', onTouchStart);
	                this._domElement.removeEventListener('pointercancel', onPointerUp);
	                this._domElement.removeEventListener('wheel', onMouseWheel, { passive: false });
	                this._domElement.removeEventListener('contextmenu', onContextMenu);
	                this._domElement.ownerDocument.removeEventListener('pointermove', onPointerMove, { passive: false });
	                this._domElement.ownerDocument.removeEventListener('mousemove', onMouseMove);
	                this._domElement.ownerDocument.removeEventListener('touchmove', onTouchMove, { passive: false });
	                this._domElement.ownerDocument.removeEventListener('pointerup', onPointerUp);
	                this._domElement.ownerDocument.removeEventListener('mouseup', onMouseUp);
	                this._domElement.ownerDocument.removeEventListener('touchend', onTouchEnd);
	            };
	            this.cancel = () => {
	                if (this._state === ACTION.NONE)
	                    return;
	                this._state = ACTION.NONE;
	                this._activePointers.length = 0;
	                endDragging();
	            };
	        }
	        this.update(0);
	    }
	    static install(libs) {
	        THREE = libs.THREE;
	        _ORIGIN = Object.freeze(new THREE.Vector3(0, 0, 0));
	        _AXIS_Y = Object.freeze(new THREE.Vector3(0, 1, 0));
	        _AXIS_Z = Object.freeze(new THREE.Vector3(0, 0, 1));
	        _v2 = new THREE.Vector2();
	        _v3A = new THREE.Vector3();
	        _v3B = new THREE.Vector3();
	        _v3C = new THREE.Vector3();
	        _xColumn = new THREE.Vector3();
	        _yColumn = new THREE.Vector3();
	        _zColumn = new THREE.Vector3();
	        _sphericalA = new THREE.Spherical();
	        _sphericalB = new THREE.Spherical();
	        _box3A = new THREE.Box3();
	        _box3B = new THREE.Box3();
	        _sphere = new THREE.Sphere();
	        _quaternionA = new THREE.Quaternion();
	        _quaternionB = new THREE.Quaternion();
	        _rotationMatrix = new THREE.Matrix4();
	        _raycaster = new THREE.Raycaster();
	    }
	    static get ACTION() {
	        return readonlyACTION;
	    }
	    get camera() {
	        return this._camera;
	    }
	    set camera(camera) {
	        this._camera = camera;
	        this.updateCameraUp();
	        this._camera.updateProjectionMatrix();
	        this._updateNearPlaneCorners();
	        this._needsUpdate = true;
	    }
	    get enabled() {
	        return this._enabled;
	    }
	    set enabled(enabled) {
	        this._enabled = enabled;
	        if (enabled) {
	            this._domElement.style.touchAction = 'none';
	            this._domElement.style.userSelect = 'none';
	            this._domElement.style.webkitUserSelect = 'none';
	        }
	        else {
	            this.cancel();
	            this._domElement.style.touchAction = '';
	            this._domElement.style.userSelect = '';
	            this._domElement.style.webkitUserSelect = '';
	        }
	    }
	    get active() {
	        return !this._hasRested;
	    }
	    get currentAction() {
	        return this._state;
	    }
	    get distance() {
	        return this._spherical.radius;
	    }
	    set distance(distance) {
	        if (this._spherical.radius === distance &&
	            this._sphericalEnd.radius === distance)
	            return;
	        this._spherical.radius = distance;
	        this._sphericalEnd.radius = distance;
	        this._needsUpdate = true;
	    }
	    get azimuthAngle() {
	        return this._spherical.theta;
	    }
	    set azimuthAngle(azimuthAngle) {
	        if (this._spherical.theta === azimuthAngle &&
	            this._sphericalEnd.theta === azimuthAngle)
	            return;
	        this._spherical.theta = azimuthAngle;
	        this._sphericalEnd.theta = azimuthAngle;
	        this._needsUpdate = true;
	    }
	    get polarAngle() {
	        return this._spherical.phi;
	    }
	    set polarAngle(polarAngle) {
	        if (this._spherical.phi === polarAngle &&
	            this._sphericalEnd.phi === polarAngle)
	            return;
	        this._spherical.phi = polarAngle;
	        this._sphericalEnd.phi = polarAngle;
	        this._needsUpdate = true;
	    }
	    get boundaryEnclosesCamera() {
	        return this._boundaryEnclosesCamera;
	    }
	    set boundaryEnclosesCamera(boundaryEnclosesCamera) {
	        this._boundaryEnclosesCamera = boundaryEnclosesCamera;
	        this._needsUpdate = true;
	    }
	    addEventListener(type, listener) {
	        super.addEventListener(type, listener);
	    }
	    removeEventListener(type, listener) {
	        super.removeEventListener(type, listener);
	    }
	    rotate(azimuthAngle, polarAngle, enableTransition = false) {
	        return this.rotateTo(this._sphericalEnd.theta + azimuthAngle, this._sphericalEnd.phi + polarAngle, enableTransition);
	    }
	    rotateAzimuthTo(azimuthAngle, enableTransition = false) {
	        return this.rotateTo(azimuthAngle, this._sphericalEnd.phi, enableTransition);
	    }
	    rotatePolarTo(polarAngle, enableTransition = false) {
	        return this.rotateTo(this._sphericalEnd.theta, polarAngle, enableTransition);
	    }
	    rotateTo(azimuthAngle, polarAngle, enableTransition = false) {
	        const theta = THREE.MathUtils.clamp(azimuthAngle, this.minAzimuthAngle, this.maxAzimuthAngle);
	        const phi = THREE.MathUtils.clamp(polarAngle, this.minPolarAngle, this.maxPolarAngle);
	        this._sphericalEnd.theta = theta;
	        this._sphericalEnd.phi = phi;
	        this._sphericalEnd.makeSafe();
	        this._needsUpdate = true;
	        if (!enableTransition) {
	            this._spherical.theta = this._sphericalEnd.theta;
	            this._spherical.phi = this._sphericalEnd.phi;
	        }
	        const resolveImmediately = !enableTransition ||
	            approxEquals(this._spherical.theta, this._sphericalEnd.theta, this.restThreshold) &&
	                approxEquals(this._spherical.phi, this._sphericalEnd.phi, this.restThreshold);
	        return this._createOnRestPromise(resolveImmediately);
	    }
	    dolly(distance, enableTransition = false) {
	        return this.dollyTo(this._sphericalEnd.radius - distance, enableTransition);
	    }
	    dollyTo(distance, enableTransition = false) {
	        const lastRadius = this._sphericalEnd.radius;
	        const newRadius = THREE.MathUtils.clamp(distance, this.minDistance, this.maxDistance);
	        const hasCollider = this.colliderMeshes.length >= 1;
	        if (hasCollider) {
	            const maxDistanceByCollisionTest = this._collisionTest();
	            const isCollided = approxEquals(maxDistanceByCollisionTest, this._spherical.radius);
	            const isDollyIn = lastRadius > newRadius;
	            if (!isDollyIn && isCollided)
	                return Promise.resolve();
	            this._sphericalEnd.radius = Math.min(newRadius, maxDistanceByCollisionTest);
	        }
	        else {
	            this._sphericalEnd.radius = newRadius;
	        }
	        this._needsUpdate = true;
	        if (!enableTransition) {
	            this._spherical.radius = this._sphericalEnd.radius;
	        }
	        const resolveImmediately = !enableTransition || approxEquals(this._spherical.radius, this._sphericalEnd.radius, this.restThreshold);
	        return this._createOnRestPromise(resolveImmediately);
	    }
	    zoom(zoomStep, enableTransition = false) {
	        return this.zoomTo(this._zoomEnd + zoomStep, enableTransition);
	    }
	    zoomTo(zoom, enableTransition = false) {
	        this._zoomEnd = THREE.MathUtils.clamp(zoom, this.minZoom, this.maxZoom);
	        this._needsUpdate = true;
	        if (!enableTransition) {
	            this._zoom = this._zoomEnd;
	        }
	        const resolveImmediately = !enableTransition || approxEquals(this._zoom, this._zoomEnd, this.restThreshold);
	        return this._createOnRestPromise(resolveImmediately);
	    }
	    pan(x, y, enableTransition = false) {
	        console.warn('`pan` has been renamed to `truck`');
	        return this.truck(x, y, enableTransition);
	    }
	    truck(x, y, enableTransition = false) {
	        this._camera.updateMatrix();
	        _xColumn.setFromMatrixColumn(this._camera.matrix, 0);
	        _yColumn.setFromMatrixColumn(this._camera.matrix, 1);
	        _xColumn.multiplyScalar(x);
	        _yColumn.multiplyScalar(-y);
	        const offset = _v3A.copy(_xColumn).add(_yColumn);
	        const to = _v3B.copy(this._targetEnd).add(offset);
	        return this.moveTo(to.x, to.y, to.z, enableTransition);
	    }
	    forward(distance, enableTransition = false) {
	        _v3A.setFromMatrixColumn(this._camera.matrix, 0);
	        _v3A.crossVectors(this._camera.up, _v3A);
	        _v3A.multiplyScalar(distance);
	        const to = _v3B.copy(this._targetEnd).add(_v3A);
	        return this.moveTo(to.x, to.y, to.z, enableTransition);
	    }
	    moveTo(x, y, z, enableTransition = false) {
	        const offset = _v3A.set(x, y, z).sub(this._targetEnd);
	        this._encloseToBoundary(this._targetEnd, offset, this.boundaryFriction);
	        this._needsUpdate = true;
	        if (!enableTransition) {
	            this._target.copy(this._targetEnd);
	        }
	        const resolveImmediately = !enableTransition ||
	            approxEquals(this._target.x, this._targetEnd.x, this.restThreshold) &&
	                approxEquals(this._target.y, this._targetEnd.y, this.restThreshold) &&
	                approxEquals(this._target.z, this._targetEnd.z, this.restThreshold);
	        return this._createOnRestPromise(resolveImmediately);
	    }
	    fitToBox(box3OrObject, enableTransition, { paddingLeft = 0, paddingRight = 0, paddingBottom = 0, paddingTop = 0 } = {}) {
	        const promises = [];
	        const aabb = box3OrObject.isBox3
	            ? _box3A.copy(box3OrObject)
	            : _box3A.setFromObject(box3OrObject);
	        if (aabb.isEmpty()) {
	            console.warn('camera-controls: fitTo() cannot be used with an empty box. Aborting');
	            Promise.resolve();
	        }
	        const theta = roundToStep(this._sphericalEnd.theta, PI_HALF);
	        const phi = roundToStep(this._sphericalEnd.phi, PI_HALF);
	        promises.push(this.rotateTo(theta, phi, enableTransition));
	        const normal = _v3A.setFromSpherical(this._sphericalEnd).normalize();
	        const rotation = _quaternionA.setFromUnitVectors(normal, _AXIS_Z);
	        const viewFromPolar = approxEquals(Math.abs(normal.y), 1);
	        if (viewFromPolar) {
	            rotation.multiply(_quaternionB.setFromAxisAngle(_AXIS_Y, theta));
	        }
	        const bb = _box3B.makeEmpty();
	        _v3B.copy(aabb.min).applyQuaternion(rotation);
	        bb.expandByPoint(_v3B);
	        _v3B.copy(aabb.min).setX(aabb.max.x).applyQuaternion(rotation);
	        bb.expandByPoint(_v3B);
	        _v3B.copy(aabb.min).setY(aabb.max.y).applyQuaternion(rotation);
	        bb.expandByPoint(_v3B);
	        _v3B.copy(aabb.max).setZ(aabb.min.z).applyQuaternion(rotation);
	        bb.expandByPoint(_v3B);
	        _v3B.copy(aabb.min).setZ(aabb.max.z).applyQuaternion(rotation);
	        bb.expandByPoint(_v3B);
	        _v3B.copy(aabb.max).setY(aabb.min.y).applyQuaternion(rotation);
	        bb.expandByPoint(_v3B);
	        _v3B.copy(aabb.max).setX(aabb.min.x).applyQuaternion(rotation);
	        bb.expandByPoint(_v3B);
	        _v3B.copy(aabb.max).applyQuaternion(rotation);
	        bb.expandByPoint(_v3B);
	        rotation.setFromUnitVectors(_AXIS_Z, normal);
	        bb.min.x -= paddingLeft;
	        bb.min.y -= paddingBottom;
	        bb.max.x += paddingRight;
	        bb.max.y += paddingTop;
	        const bbSize = bb.getSize(_v3A);
	        const center = bb.getCenter(_v3B).applyQuaternion(rotation);
	        if (isPerspectiveCamera(this._camera)) {
	            const distance = this.getDistanceToFitBox(bbSize.x, bbSize.y, bbSize.z);
	            promises.push(this.moveTo(center.x, center.y, center.z, enableTransition));
	            promises.push(this.dollyTo(distance, enableTransition));
	            promises.push(this.setFocalOffset(0, 0, 0, enableTransition));
	        }
	        else if (isOrthographicCamera(this._camera)) {
	            const camera = this._camera;
	            const width = camera.right - camera.left;
	            const height = camera.top - camera.bottom;
	            const zoom = Math.min(width / bbSize.x, height / bbSize.y);
	            promises.push(this.moveTo(center.x, center.y, center.z, enableTransition));
	            promises.push(this.zoomTo(zoom, enableTransition));
	            promises.push(this.setFocalOffset(0, 0, 0, enableTransition));
	        }
	        return Promise.all(promises);
	    }
	    fitTo(box3OrObject, enableTransition, fitToOptions = {}) {
	        console.warn('camera-controls: fitTo() has been renamed to fitToBox()');
	        return this.fitToBox(box3OrObject, enableTransition, fitToOptions);
	    }
	    fitToSphere(sphereOrMesh, enableTransition) {
	        const promises = [];
	        const isSphere = sphereOrMesh instanceof THREE.Sphere;
	        const boundingSphere = isSphere ?
	            _sphere.copy(sphereOrMesh) :
	            createBoundingSphere(sphereOrMesh, _sphere);
	        promises.push(this.moveTo(boundingSphere.center.x, boundingSphere.center.y, boundingSphere.center.z, enableTransition));
	        if (isPerspectiveCamera(this._camera)) {
	            const distanceToFit = this.getDistanceToFitSphere(boundingSphere.radius);
	            promises.push(this.dollyTo(distanceToFit, enableTransition));
	        }
	        else if (isOrthographicCamera(this._camera)) {
	            const width = this._camera.right - this._camera.left;
	            const height = this._camera.top - this._camera.bottom;
	            const diameter = 2 * boundingSphere.radius;
	            const zoom = Math.min(width / diameter, height / diameter);
	            promises.push(this.zoomTo(zoom, enableTransition));
	        }
	        promises.push(this.setFocalOffset(0, 0, 0, enableTransition));
	        return Promise.all(promises);
	    }
	    setLookAt(positionX, positionY, positionZ, targetX, targetY, targetZ, enableTransition = false) {
	        const target = _v3B.set(targetX, targetY, targetZ);
	        const position = _v3A.set(positionX, positionY, positionZ);
	        this._targetEnd.copy(target);
	        this._sphericalEnd.setFromVector3(position.sub(target).applyQuaternion(this._yAxisUpSpace));
	        this.normalizeRotations();
	        this._needsUpdate = true;
	        if (!enableTransition) {
	            this._target.copy(this._targetEnd);
	            this._spherical.copy(this._sphericalEnd);
	        }
	        const resolveImmediately = !enableTransition ||
	            approxEquals(this._target.x, this._targetEnd.x, this.restThreshold) &&
	                approxEquals(this._target.y, this._targetEnd.y, this.restThreshold) &&
	                approxEquals(this._target.z, this._targetEnd.z, this.restThreshold) &&
	                approxEquals(this._spherical.theta, this._sphericalEnd.theta, this.restThreshold) &&
	                approxEquals(this._spherical.phi, this._sphericalEnd.phi, this.restThreshold) &&
	                approxEquals(this._spherical.radius, this._sphericalEnd.radius, this.restThreshold);
	        return this._createOnRestPromise(resolveImmediately);
	    }
	    lerpLookAt(positionAX, positionAY, positionAZ, targetAX, targetAY, targetAZ, positionBX, positionBY, positionBZ, targetBX, targetBY, targetBZ, t, enableTransition = false) {
	        const targetA = _v3A.set(targetAX, targetAY, targetAZ);
	        const positionA = _v3B.set(positionAX, positionAY, positionAZ);
	        _sphericalA.setFromVector3(positionA.sub(targetA).applyQuaternion(this._yAxisUpSpace));
	        const targetB = _v3C.set(targetBX, targetBY, targetBZ);
	        const positionB = _v3B.set(positionBX, positionBY, positionBZ);
	        _sphericalB.setFromVector3(positionB.sub(targetB).applyQuaternion(this._yAxisUpSpace));
	        this._targetEnd.copy(targetA.lerp(targetB, t));
	        const deltaTheta = _sphericalB.theta - _sphericalA.theta;
	        const deltaPhi = _sphericalB.phi - _sphericalA.phi;
	        const deltaRadius = _sphericalB.radius - _sphericalA.radius;
	        this._sphericalEnd.set(_sphericalA.radius + deltaRadius * t, _sphericalA.phi + deltaPhi * t, _sphericalA.theta + deltaTheta * t);
	        this.normalizeRotations();
	        this._needsUpdate = true;
	        if (!enableTransition) {
	            this._target.copy(this._targetEnd);
	            this._spherical.copy(this._sphericalEnd);
	        }
	        const resolveImmediately = !enableTransition ||
	            approxEquals(this._target.x, this._targetEnd.x, this.restThreshold) &&
	                approxEquals(this._target.y, this._targetEnd.y, this.restThreshold) &&
	                approxEquals(this._target.z, this._targetEnd.z, this.restThreshold) &&
	                approxEquals(this._spherical.theta, this._sphericalEnd.theta, this.restThreshold) &&
	                approxEquals(this._spherical.phi, this._sphericalEnd.phi, this.restThreshold) &&
	                approxEquals(this._spherical.radius, this._sphericalEnd.radius, this.restThreshold);
	        return this._createOnRestPromise(resolveImmediately);
	    }
	    setPosition(positionX, positionY, positionZ, enableTransition = false) {
	        return this.setLookAt(positionX, positionY, positionZ, this._targetEnd.x, this._targetEnd.y, this._targetEnd.z, enableTransition);
	    }
	    setTarget(targetX, targetY, targetZ, enableTransition = false) {
	        const pos = this.getPosition(_v3A);
	        return this.setLookAt(pos.x, pos.y, pos.z, targetX, targetY, targetZ, enableTransition);
	    }
	    setFocalOffset(x, y, z, enableTransition = false) {
	        this._focalOffsetEnd.set(x, y, z);
	        this._needsUpdate = true;
	        if (!enableTransition) {
	            this._focalOffset.copy(this._focalOffsetEnd);
	        }
	        const resolveImmediately = !enableTransition ||
	            approxEquals(this._focalOffset.x, this._focalOffsetEnd.x, this.restThreshold) &&
	                approxEquals(this._focalOffset.y, this._focalOffsetEnd.y, this.restThreshold) &&
	                approxEquals(this._focalOffset.z, this._focalOffsetEnd.z, this.restThreshold);
	        return this._createOnRestPromise(resolveImmediately);
	    }
	    setOrbitPoint(targetX, targetY, targetZ) {
	        _xColumn.setFromMatrixColumn(this._camera.matrixWorldInverse, 0);
	        _yColumn.setFromMatrixColumn(this._camera.matrixWorldInverse, 1);
	        _zColumn.setFromMatrixColumn(this._camera.matrixWorldInverse, 2);
	        const position = _v3A.set(targetX, targetY, targetZ);
	        const distance = position.distanceTo(this._camera.position);
	        const cameraToPoint = position.sub(this._camera.position);
	        _xColumn.multiplyScalar(cameraToPoint.x);
	        _yColumn.multiplyScalar(cameraToPoint.y);
	        _zColumn.multiplyScalar(cameraToPoint.z);
	        _v3A.copy(_xColumn).add(_yColumn).add(_zColumn);
	        _v3A.z = _v3A.z + distance;
	        this.dollyTo(distance, false);
	        this.setFocalOffset(-_v3A.x, _v3A.y, -_v3A.z, false);
	        this.moveTo(targetX, targetY, targetZ, false);
	    }
	    setBoundary(box3) {
	        if (!box3) {
	            this._boundary.min.set(-Infinity, -Infinity, -Infinity);
	            this._boundary.max.set(Infinity, Infinity, Infinity);
	            this._needsUpdate = true;
	            return;
	        }
	        this._boundary.copy(box3);
	        this._boundary.clampPoint(this._targetEnd, this._targetEnd);
	        this._needsUpdate = true;
	    }
	    setViewport(viewportOrX, y, width, height) {
	        if (viewportOrX === null) {
	            this._viewport = null;
	            return;
	        }
	        this._viewport = this._viewport || new THREE.Vector4();
	        if (typeof viewportOrX === 'number') {
	            this._viewport.set(viewportOrX, y, width, height);
	        }
	        else {
	            this._viewport.copy(viewportOrX);
	        }
	    }
	    getDistanceToFitBox(width, height, depth) {
	        if (notSupportedInOrthographicCamera(this._camera, 'getDistanceToFitBox'))
	            return this._spherical.radius;
	        const boundingRectAspect = width / height;
	        const fov = this._camera.getEffectiveFOV() * THREE.MathUtils.DEG2RAD;
	        const aspect = this._camera.aspect;
	        const heightToFit = boundingRectAspect < aspect ? height : width / aspect;
	        return heightToFit * 0.5 / Math.tan(fov * 0.5) + depth * 0.5;
	    }
	    getDistanceToFit(width, height, depth) {
	        console.warn('camera-controls: getDistanceToFit() has been renamed to getDistanceToFitBox()');
	        return this.getDistanceToFitBox(width, height, depth);
	    }
	    getDistanceToFitSphere(radius) {
	        if (notSupportedInOrthographicCamera(this._camera, 'getDistanceToFitSphere'))
	            return this._spherical.radius;
	        const vFOV = this._camera.getEffectiveFOV() * THREE.MathUtils.DEG2RAD;
	        const hFOV = Math.atan(Math.tan(vFOV * 0.5) * this._camera.aspect) * 2;
	        const fov = 1 < this._camera.aspect ? vFOV : hFOV;
	        return radius / (Math.sin(fov * 0.5));
	    }
	    getTarget(out) {
	        const _out = !!out && out.isVector3 ? out : new THREE.Vector3();
	        return _out.copy(this._targetEnd);
	    }
	    getPosition(out) {
	        const _out = !!out && out.isVector3 ? out : new THREE.Vector3();
	        return _out.setFromSpherical(this._sphericalEnd).applyQuaternion(this._yAxisUpSpaceInverse).add(this._targetEnd);
	    }
	    getFocalOffset(out) {
	        const _out = !!out && out.isVector3 ? out : new THREE.Vector3();
	        return _out.copy(this._focalOffsetEnd);
	    }
	    normalizeRotations() {
	        this._sphericalEnd.theta = this._sphericalEnd.theta % PI_2;
	        if (this._sphericalEnd.theta < 0)
	            this._sphericalEnd.theta += PI_2;
	        this._spherical.theta += PI_2 * Math.round((this._sphericalEnd.theta - this._spherical.theta) / PI_2);
	    }
	    reset(enableTransition = false) {
	        const promises = [
	            this.setLookAt(this._position0.x, this._position0.y, this._position0.z, this._target0.x, this._target0.y, this._target0.z, enableTransition),
	            this.setFocalOffset(this._focalOffset0.x, this._focalOffset0.y, this._focalOffset0.z, enableTransition),
	            this.zoomTo(this._zoom0, enableTransition),
	        ];
	        return Promise.all(promises);
	    }
	    saveState() {
	        this._target0.copy(this._target);
	        this._position0.copy(this._camera.position);
	        this._zoom0 = this._zoom;
	    }
	    updateCameraUp() {
	        this._yAxisUpSpace.setFromUnitVectors(this._camera.up, _AXIS_Y);
	        quatInvertCompat(this._yAxisUpSpaceInverse.copy(this._yAxisUpSpace));
	    }
	    update(delta) {
	        const dampingFactor = this._state === ACTION.NONE ? this.dampingFactor : this.draggingDampingFactor;
	        const lerpRatio = Math.min(dampingFactor * delta * 60, 1);
	        const deltaTheta = this._sphericalEnd.theta - this._spherical.theta;
	        const deltaPhi = this._sphericalEnd.phi - this._spherical.phi;
	        const deltaRadius = this._sphericalEnd.radius - this._spherical.radius;
	        const deltaTarget = _v3A.subVectors(this._targetEnd, this._target);
	        const deltaOffset = _v3B.subVectors(this._focalOffsetEnd, this._focalOffset);
	        if (!approxZero(deltaTheta) ||
	            !approxZero(deltaPhi) ||
	            !approxZero(deltaRadius) ||
	            !approxZero(deltaTarget.x) ||
	            !approxZero(deltaTarget.y) ||
	            !approxZero(deltaTarget.z) ||
	            !approxZero(deltaOffset.x) ||
	            !approxZero(deltaOffset.y) ||
	            !approxZero(deltaOffset.z)) {
	            this._spherical.set(this._spherical.radius + deltaRadius * lerpRatio, this._spherical.phi + deltaPhi * lerpRatio, this._spherical.theta + deltaTheta * lerpRatio);
	            this._target.add(deltaTarget.multiplyScalar(lerpRatio));
	            this._focalOffset.add(deltaOffset.multiplyScalar(lerpRatio));
	            this._needsUpdate = true;
	        }
	        else {
	            this._spherical.copy(this._sphericalEnd);
	            this._target.copy(this._targetEnd);
	            this._focalOffset.copy(this._focalOffsetEnd);
	        }
	        if (this._dollyControlAmount !== 0) {
	            if (isPerspectiveCamera(this._camera)) {
	                const camera = this._camera;
	                const direction = _v3A.setFromSpherical(this._sphericalEnd).applyQuaternion(this._yAxisUpSpaceInverse).normalize().negate();
	                const planeX = _v3B.copy(direction).cross(camera.up).normalize();
	                if (planeX.lengthSq() === 0)
	                    planeX.x = 1.0;
	                const planeY = _v3C.crossVectors(planeX, direction);
	                const worldToScreen = this._sphericalEnd.radius * Math.tan(camera.getEffectiveFOV() * THREE.MathUtils.DEG2RAD * 0.5);
	                const prevRadius = this._sphericalEnd.radius - this._dollyControlAmount;
	                const lerpRatio = (prevRadius - this._sphericalEnd.radius) / this._sphericalEnd.radius;
	                const cursor = _v3A.copy(this._targetEnd)
	                    .add(planeX.multiplyScalar(this._dollyControlCoord.x * worldToScreen * camera.aspect))
	                    .add(planeY.multiplyScalar(this._dollyControlCoord.y * worldToScreen));
	                this._targetEnd.lerp(cursor, lerpRatio);
	                this._target.copy(this._targetEnd);
	            }
	            else if (isOrthographicCamera(this._camera)) {
	                const camera = this._camera;
	                const worldPosition = _v3A.set(this._dollyControlCoord.x, this._dollyControlCoord.y, (camera.near + camera.far) / (camera.near - camera.far)).unproject(camera);
	                const quaternion = _v3B.set(0, 0, -1).applyQuaternion(camera.quaternion);
	                const divisor = quaternion.dot(camera.up);
	                const distance = approxZero(divisor) ? -worldPosition.dot(camera.up) : -worldPosition.dot(camera.up) / divisor;
	                const cursor = _v3C.copy(worldPosition).add(quaternion.multiplyScalar(distance));
	                this._targetEnd.lerp(cursor, 1 - camera.zoom / this._dollyControlAmount);
	                this._target.copy(this._targetEnd);
	            }
	            this._dollyControlAmount = 0;
	        }
	        const maxDistance = this._collisionTest();
	        this._spherical.radius = Math.min(this._spherical.radius, maxDistance);
	        this._spherical.makeSafe();
	        this._camera.position.setFromSpherical(this._spherical).applyQuaternion(this._yAxisUpSpaceInverse).add(this._target);
	        this._camera.lookAt(this._target);
	        const affectOffset = !approxZero(this._focalOffset.x) ||
	            !approxZero(this._focalOffset.y) ||
	            !approxZero(this._focalOffset.z);
	        if (affectOffset) {
	            this._camera.updateMatrix();
	            _xColumn.setFromMatrixColumn(this._camera.matrix, 0);
	            _yColumn.setFromMatrixColumn(this._camera.matrix, 1);
	            _zColumn.setFromMatrixColumn(this._camera.matrix, 2);
	            _xColumn.multiplyScalar(this._focalOffset.x);
	            _yColumn.multiplyScalar(-this._focalOffset.y);
	            _zColumn.multiplyScalar(this._focalOffset.z);
	            _v3A.copy(_xColumn).add(_yColumn).add(_zColumn);
	            this._camera.position.add(_v3A);
	        }
	        if (this._boundaryEnclosesCamera) {
	            this._encloseToBoundary(this._camera.position.copy(this._target), _v3A.setFromSpherical(this._spherical).applyQuaternion(this._yAxisUpSpaceInverse), 1.0);
	        }
	        const zoomDelta = this._zoomEnd - this._zoom;
	        this._zoom += zoomDelta * lerpRatio;
	        if (this._camera.zoom !== this._zoom) {
	            if (approxZero(zoomDelta))
	                this._zoom = this._zoomEnd;
	            this._camera.zoom = this._zoom;
	            this._camera.updateProjectionMatrix();
	            this._updateNearPlaneCorners();
	            this._needsUpdate = true;
	        }
	        const updated = this._needsUpdate;
	        if (updated && !this._updatedLastTime) {
	            this._hasRested = false;
	            this.dispatchEvent({ type: 'wake' });
	            this.dispatchEvent({ type: 'update' });
	        }
	        else if (updated) {
	            this.dispatchEvent({ type: 'update' });
	            if (approxZero(deltaTheta, this.restThreshold) &&
	                approxZero(deltaPhi, this.restThreshold) &&
	                approxZero(deltaRadius, this.restThreshold) &&
	                approxZero(deltaTarget.x, this.restThreshold) &&
	                approxZero(deltaTarget.y, this.restThreshold) &&
	                approxZero(deltaTarget.z, this.restThreshold) &&
	                approxZero(deltaOffset.x, this.restThreshold) &&
	                approxZero(deltaOffset.y, this.restThreshold) &&
	                approxZero(deltaOffset.z, this.restThreshold) &&
	                !this._hasRested) {
	                this._hasRested = true;
	                this.dispatchEvent({ type: 'rest' });
	            }
	        }
	        else if (!updated && this._updatedLastTime) {
	            this.dispatchEvent({ type: 'sleep' });
	        }
	        this._updatedLastTime = updated;
	        this._needsUpdate = false;
	        return updated;
	    }
	    toJSON() {
	        return JSON.stringify({
	            enabled: this._enabled,
	            minDistance: this.minDistance,
	            maxDistance: infinityToMaxNumber(this.maxDistance),
	            minZoom: this.minZoom,
	            maxZoom: infinityToMaxNumber(this.maxZoom),
	            minPolarAngle: this.minPolarAngle,
	            maxPolarAngle: infinityToMaxNumber(this.maxPolarAngle),
	            minAzimuthAngle: infinityToMaxNumber(this.minAzimuthAngle),
	            maxAzimuthAngle: infinityToMaxNumber(this.maxAzimuthAngle),
	            dampingFactor: this.dampingFactor,
	            draggingDampingFactor: this.draggingDampingFactor,
	            dollySpeed: this.dollySpeed,
	            truckSpeed: this.truckSpeed,
	            dollyToCursor: this.dollyToCursor,
	            verticalDragToForward: this.verticalDragToForward,
	            target: this._targetEnd.toArray(),
	            position: _v3A.setFromSpherical(this._sphericalEnd).add(this._targetEnd).toArray(),
	            zoom: this._zoomEnd,
	            focalOffset: this._focalOffsetEnd.toArray(),
	            target0: this._target0.toArray(),
	            position0: this._position0.toArray(),
	            zoom0: this._zoom0,
	            focalOffset0: this._focalOffset0.toArray(),
	        });
	    }
	    fromJSON(json, enableTransition = false) {
	        const obj = JSON.parse(json);
	        const position = _v3A.fromArray(obj.position);
	        this.enabled = obj.enabled;
	        this.minDistance = obj.minDistance;
	        this.maxDistance = maxNumberToInfinity(obj.maxDistance);
	        this.minZoom = obj.minZoom;
	        this.maxZoom = maxNumberToInfinity(obj.maxZoom);
	        this.minPolarAngle = obj.minPolarAngle;
	        this.maxPolarAngle = maxNumberToInfinity(obj.maxPolarAngle);
	        this.minAzimuthAngle = maxNumberToInfinity(obj.minAzimuthAngle);
	        this.maxAzimuthAngle = maxNumberToInfinity(obj.maxAzimuthAngle);
	        this.dampingFactor = obj.dampingFactor;
	        this.draggingDampingFactor = obj.draggingDampingFactor;
	        this.dollySpeed = obj.dollySpeed;
	        this.truckSpeed = obj.truckSpeed;
	        this.dollyToCursor = obj.dollyToCursor;
	        this.verticalDragToForward = obj.verticalDragToForward;
	        this._target0.fromArray(obj.target0);
	        this._position0.fromArray(obj.position0);
	        this._zoom0 = obj.zoom0;
	        this._focalOffset0.fromArray(obj.focalOffset0);
	        this.moveTo(obj.target[0], obj.target[1], obj.target[2], enableTransition);
	        _sphericalA.setFromVector3(position.sub(this._targetEnd).applyQuaternion(this._yAxisUpSpace));
	        this.rotateTo(_sphericalA.theta, _sphericalA.phi, enableTransition);
	        this.zoomTo(obj.zoom, enableTransition);
	        this.setFocalOffset(obj.focalOffset[0], obj.focalOffset[1], obj.focalOffset[2], enableTransition);
	        this._needsUpdate = true;
	    }
	    dispose() {
	        this._removeAllEventListeners();
	    }
	    _findPointerById(pointerId) {
	        let pointer = null;
	        this._activePointers.some((activePointer) => {
	            if (activePointer.pointerId === pointerId) {
	                pointer = activePointer;
	                return true;
	            }
	            return false;
	        });
	        return pointer;
	    }
	    _encloseToBoundary(position, offset, friction) {
	        const offsetLength2 = offset.lengthSq();
	        if (offsetLength2 === 0.0) {
	            return position;
	        }
	        const newTarget = _v3B.copy(offset).add(position);
	        const clampedTarget = this._boundary.clampPoint(newTarget, _v3C);
	        const deltaClampedTarget = clampedTarget.sub(newTarget);
	        const deltaClampedTargetLength2 = deltaClampedTarget.lengthSq();
	        if (deltaClampedTargetLength2 === 0.0) {
	            return position.add(offset);
	        }
	        else if (deltaClampedTargetLength2 === offsetLength2) {
	            return position;
	        }
	        else if (friction === 0.0) {
	            return position.add(offset).add(deltaClampedTarget);
	        }
	        else {
	            const offsetFactor = 1.0 + friction * deltaClampedTargetLength2 / offset.dot(deltaClampedTarget);
	            return position
	                .add(_v3B.copy(offset).multiplyScalar(offsetFactor))
	                .add(deltaClampedTarget.multiplyScalar(1.0 - friction));
	        }
	    }
	    _updateNearPlaneCorners() {
	        if (isPerspectiveCamera(this._camera)) {
	            const camera = this._camera;
	            const near = camera.near;
	            const fov = camera.getEffectiveFOV() * THREE.MathUtils.DEG2RAD;
	            const heightHalf = Math.tan(fov * 0.5) * near;
	            const widthHalf = heightHalf * camera.aspect;
	            this._nearPlaneCorners[0].set(-widthHalf, -heightHalf, 0);
	            this._nearPlaneCorners[1].set(widthHalf, -heightHalf, 0);
	            this._nearPlaneCorners[2].set(widthHalf, heightHalf, 0);
	            this._nearPlaneCorners[3].set(-widthHalf, heightHalf, 0);
	        }
	        else if (isOrthographicCamera(this._camera)) {
	            const camera = this._camera;
	            const zoomInv = 1 / camera.zoom;
	            const left = camera.left * zoomInv;
	            const right = camera.right * zoomInv;
	            const top = camera.top * zoomInv;
	            const bottom = camera.bottom * zoomInv;
	            this._nearPlaneCorners[0].set(left, top, 0);
	            this._nearPlaneCorners[1].set(right, top, 0);
	            this._nearPlaneCorners[2].set(right, bottom, 0);
	            this._nearPlaneCorners[3].set(left, bottom, 0);
	        }
	    }
	    _collisionTest() {
	        let distance = Infinity;
	        const hasCollider = this.colliderMeshes.length >= 1;
	        if (!hasCollider)
	            return distance;
	        if (notSupportedInOrthographicCamera(this._camera, '_collisionTest'))
	            return distance;
	        const direction = _v3A.setFromSpherical(this._spherical).divideScalar(this._spherical.radius);
	        _rotationMatrix.lookAt(_ORIGIN, direction, this._camera.up);
	        for (let i = 0; i < 4; i++) {
	            const nearPlaneCorner = _v3B.copy(this._nearPlaneCorners[i]);
	            nearPlaneCorner.applyMatrix4(_rotationMatrix);
	            const origin = _v3C.addVectors(this._target, nearPlaneCorner);
	            _raycaster.set(origin, direction);
	            _raycaster.far = this._spherical.radius + 1;
	            const intersects = _raycaster.intersectObjects(this.colliderMeshes);
	            if (intersects.length !== 0 && intersects[0].distance < distance) {
	                distance = intersects[0].distance;
	            }
	        }
	        return distance;
	    }
	    _getClientRect(target) {
	        const rect = this._domElement.getBoundingClientRect();
	        target.x = rect.left;
	        target.y = rect.top;
	        if (this._viewport) {
	            target.x += this._viewport.x;
	            target.y += rect.height - this._viewport.w - this._viewport.y;
	            target.width = this._viewport.z;
	            target.height = this._viewport.w;
	        }
	        else {
	            target.width = rect.width;
	            target.height = rect.height;
	        }
	        return target;
	    }
	    _createOnRestPromise(resolveImmediately) {
	        if (resolveImmediately)
	            return Promise.resolve();
	        this._hasRested = false;
	        this.dispatchEvent({ type: 'transitionstart' });
	        return new Promise((resolve) => {
	            const onResolve = () => {
	                this.removeEventListener('rest', onResolve);
	                resolve();
	            };
	            this.addEventListener('rest', onResolve);
	        });
	    }
	    _removeAllEventListeners() { }
	}
	function createBoundingSphere(object3d, out) {
	    const boundingSphere = out;
	    const center = boundingSphere.center;
	    object3d.traverse((object) => {
	        if (!object.isMesh)
	            return;
	        _box3A.expandByObject(object);
	    });
	    _box3A.getCenter(center);
	    let maxRadiusSq = 0;
	    object3d.traverse((object) => {
	        if (!object.isMesh)
	            return;
	        const mesh = object;
	        const geometry = mesh.geometry.clone();
	        geometry.applyMatrix4(mesh.matrixWorld);
	        if (geometry.isBufferGeometry) {
	            const bufferGeometry = geometry;
	            const position = bufferGeometry.attributes.position;
	            for (let i = 0, l = position.count; i < l; i++) {
	                _v3A.fromBufferAttribute(position, i);
	                maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(_v3A));
	            }
	        }
	        else {
	            const position = geometry.attributes.position;
	            const vector = new THREE.Vector3();
	            for (let i = 0, l = position.count; i < l; i++) {
	                vector.fromBufferAttribute(position, i);
	                maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(vector));
	            }
	        }
	    });
	    boundingSphere.radius = Math.sqrt(maxRadiusSq);
	    return boundingSphere;
	}

	onInstallHandlers.push(function () {
	  CameraControls.install({
	    THREE: THREE$1
	  });
	});
	var TPSCameraControls = /*#__PURE__*/function (_CameraControls) {
	  _inherits(TPSCameraControls, _CameraControls);

	  var _super = _createSuper(TPSCameraControls);

	  function TPSCameraControls(camera, trackObject, domElement) {
	    var _thisSuper, _this;

	    _classCallCheck(this, TPSCameraControls);

	    _this = _super.call(this, camera, domElement);
	    _this.minDistance = 1;
	    _this.maxDistance = 300;
	    _this.azimuthRotateSpeed = 0.3; // negative value to invert rotation direction

	    _this.polarRotateSpeed = -0.2; // negative value to invert rotation direction

	    _this.minPolarAngle = 30 * THREE$1.Math.DEG2RAD;
	    _this.maxPolarAngle = 120 * THREE$1.Math.DEG2RAD;
	    _this.draggingDampingFactor = 1;
	    _this.mouseButtons.right = CameraControls.ACTION.NONE;
	    _this.mouseButtons.middle = CameraControls.ACTION.NONE;
	    _this.touches.two = CameraControls.ACTION.TOUCH_DOLLY;
	    _this.touches.three = CameraControls.ACTION.TOUCH_DOLLY;
	    _this.offset = new THREE$1.Vector3(0, 1, 0);

	    _this.update = function (delta) {
	      var x = trackObject.position.x + _this.offset.x;
	      var y = trackObject.position.y + _this.offset.y;
	      var z = trackObject.position.z + _this.offset.z;

	      _this.moveTo(x, y, z, false);

	      _get((_thisSuper = _assertThisInitialized(_this), _getPrototypeOf(TPSCameraControls.prototype)), "update", _thisSuper).call(_thisSuper, delta);
	    };

	    return _this;
	  }

	  _createClass(TPSCameraControls, [{
	    key: "frontAngle",
	    get: function get() {
	      return this.azimuthAngle;
	    }
	  }]);

	  return TPSCameraControls;
	}(CameraControls);

	var index = {
	  install: install,
	  World: World,
	  Octree: Octree,
	  CharacterController: CharacterController,
	  AnimationController: AnimationController,
	  KeyInputControl: KeyInputControl,
	  TPSCameraControls: TPSCameraControls
	};

	return index;

}));