ggabcd-meshwalk
Version:
MeshWalk.js is a JS library which helps your TPS game development with three.js.
1,704 lines (1,447 loc) • 120 kB
JavaScript
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.isOrt