From a9fc4e21b6b9da24db48fc0c169c1bd87af9a688 Mon Sep 17 00:00:00 2001 From: Antonio Pisano Date: Thu, 28 Dec 2023 11:25:07 +0100 Subject: [PATCH 1/3] Init the data structures relevant for collision calculations when the body is added, rather than on the fly If a body is deleted before it can be the body0 of a collision, an entry for it won't be created in the collisionKeys array. However, because of the splice idiom used at removal, when a body that has no entry in collisionKeys is deleted, an arbitrary entry will be deleted regardless. Eventually, when further bodys are removed according to this pattern, there may be no entry in collisionKeys at all, which will prevent the collisions map from ever being emptied. This can completely prevent the firing of collisionstart events. --- src/drivers/ammo-driver.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/drivers/ammo-driver.js b/src/drivers/ammo-driver.js index feb5d0f..d23e245 100644 --- a/src/drivers/ammo-driver.js +++ b/src/drivers/ammo-driver.js @@ -61,7 +61,11 @@ AmmoDriver.prototype.init = function(worldConfig) { /* @param {Ammo.btCollisionObject} body */ AmmoDriver.prototype.addBody = function(body, group, mask) { this.physicsWorld.addRigidBody(body, group, mask); - this.els.set(Ammo.getPointer(body), body.el); + const bodyptr = Ammo.getPointer(body); + this.els.set(bodyptr, body.el); + this.collisions.set(bodyptr, []); + this.collisionKeys.push(bodyptr); + this.currentCollisions.set(bodyptr, new Set()); }; /* @param {Ammo.btCollisionObject} body */ @@ -103,10 +107,6 @@ AmmoDriver.prototype.step = function(deltaTime) { } if (collided) { - if (!this.collisions.has(body0ptr)) { - this.collisions.set(body0ptr, []); - this.collisionKeys.push(body0ptr); - } if (this.collisions.get(body0ptr).indexOf(body1ptr) === -1) { this.collisions.get(body0ptr).push(body1ptr); if (this.eventListeners.indexOf(body0ptr) !== -1) { @@ -116,9 +116,6 @@ AmmoDriver.prototype.step = function(deltaTime) { this.els.get(body1ptr).emit("collidestart", { targetEl: this.els.get(body0ptr) }); } } - if (!this.currentCollisions.has(body0ptr)) { - this.currentCollisions.set(body0ptr, new Set()); - } this.currentCollisions.get(body0ptr).add(body1ptr); } } From 18b0e69848b988731446f9420b3c292ac9a64afe Mon Sep 17 00:00:00 2001 From: Antonio Pisano Date: Thu, 28 Dec 2023 11:25:19 +0100 Subject: [PATCH 2/3] Revert previous fixes from #47 #52 and #54, as the previous commit should make them not necessary anymore --- src/components/body/ammo-body.js | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/components/body/ammo-body.js b/src/components/body/ammo-body.js index 264cce1..2c0e733 100644 --- a/src/components/body/ammo-body.js +++ b/src/components/body/ammo-body.js @@ -364,24 +364,15 @@ let AmmoBody = { if (this.triMesh) Ammo.destroy(this.triMesh); if (this.localScaling) Ammo.destroy(this.localScaling); if (this.compoundShape) Ammo.destroy(this.compoundShape); + if (this.body) { + Ammo.destroy(this.body); + delete this.body; + } Ammo.destroy(this.rbInfo); Ammo.destroy(this.msTransform); Ammo.destroy(this.motionState); Ammo.destroy(this.localInertia); Ammo.destroy(this.rotation); - if (this.body) { - if (!this.data.emitCollisionEvents) { - // As per issue 47 / PR 48 there is a strange bug - // not yet understood, where destroying an Ammo body - // leads to subsequent issues reporting collision events. - // - // So if we are reporting collision events, preferable to - // tolerate a small memory leak, by not destroying the - // Ammo body, rather than missing collision events. - Ammo.destroy(this.body); - } - delete this.body; - } }, beforeStep: function() { From fa6c457f778cfb341b1d7ce15e1d5d96e2c1efaa Mon Sep 17 00:00:00 2001 From: Antonio Pisano Date: Thu, 28 Dec 2023 11:40:18 +0100 Subject: [PATCH 3/3] Update build --- dist/aframe-physics-system.js | 7156 ++++++++++++++--------------- dist/aframe-physics-system.min.js | 2 +- 2 files changed, 3573 insertions(+), 3585 deletions(-) diff --git a/dist/aframe-physics-system.js b/dist/aframe-physics-system.js index 3b610d8..0a65254 100644 --- a/dist/aframe-physics-system.js +++ b/dist/aframe-physics-system.js @@ -1,197 +1,197 @@ (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i { - this.loadedEventFired = true; - }, - { once: true } - ); - } - - if (this.system.initialized && this.loadedEventFired) { - this.initBody(); - } - }, - - /** - * Parses an element's geometry and component metadata to create an Ammo body instance for the - * component. - */ - initBody: (function() { - const pos = new THREE.Vector3(); - const quat = new THREE.Quaternion(); - const boundingBox = new THREE.Box3(); - - return function() { - const el = this.el, - data = this.data; - const clamp = (num, min, max) => Math.min(Math.max(num, min), max) - - this.localScaling = new Ammo.btVector3(); - - const obj = this.el.object3D; - obj.getWorldPosition(pos); - obj.getWorldQuaternion(quat); - - this.prevScale = new THREE.Vector3(1, 1, 1); - this.prevNumChildShapes = 0; - - this.msTransform = new Ammo.btTransform(); - this.msTransform.setIdentity(); - this.rotation = new Ammo.btQuaternion(quat.x, quat.y, quat.z, quat.w); - - this.msTransform.getOrigin().setValue(pos.x, pos.y, pos.z); - this.msTransform.setRotation(this.rotation); - - this.motionState = new Ammo.btDefaultMotionState(this.msTransform); - - this.localInertia = new Ammo.btVector3(0, 0, 0); - - this.compoundShape = new Ammo.btCompoundShape(true); - - this.rbInfo = new Ammo.btRigidBodyConstructionInfo( - data.mass, - this.motionState, - this.compoundShape, - this.localInertia - ); - this.rbInfo.m_restitution = clamp(this.data.restitution, 0, 1); - this.body = new Ammo.btRigidBody(this.rbInfo); - this.body.setActivationState(ACTIVATION_STATES.indexOf(data.activationState) + 1); - this.body.setSleepingThresholds(data.linearSleepingThreshold, data.angularSleepingThreshold); - - this.body.setDamping(data.linearDamping, data.angularDamping); - - const angularFactor = new Ammo.btVector3(data.angularFactor.x, data.angularFactor.y, data.angularFactor.z); - this.body.setAngularFactor(angularFactor); - Ammo.destroy(angularFactor); - - this._updateBodyGravity(data.gravity) - - this.updateCollisionFlags(); - - this.el.body = this.body; - this.body.el = el; - - this.isLoaded = true; - - this.el.emit("body-loaded", { body: this.el.body }); - - this._addToSystem(); - }; - })(), - - tick: function() { - if (this.system.initialized && !this.isLoaded && this.loadedEventFired) { - this.initBody(); - } - }, - - _updateBodyGravity(gravity) { - - if (gravity.x !== undefined && - gravity.y !== undefined && - gravity.z !== undefined) { - const gravityBtVec = new Ammo.btVector3(gravity.x, gravity.y, gravity.z); - if (!almostEqualsBtVector3(0.001, gravityBtVec, this.system.driver.physicsWorld.getGravity())) { - this.body.setFlags(RIGID_BODY_FLAGS.DISABLE_WORLD_GRAVITY); - } else { - this.body.setFlags(RIGID_BODY_FLAGS.NONE); - } - this.body.setGravity(gravityBtVec); - Ammo.destroy(gravityBtVec); - } - else { - // no per-body gravity specified - just use world gravity - this.body.setFlags(RIGID_BODY_FLAGS.NONE); - } - }, - - _updateShapes: (function() { - const needsPolyhedralInitialization = [SHAPE.HULL, SHAPE.HACD, SHAPE.VHACD]; - return function() { - let updated = false; - - const obj = this.el.object3D; - if (this.data.scaleAutoUpdate && this.prevScale && !almostEqualsVector3(0.001, obj.scale, this.prevScale)) { - this.prevScale.copy(obj.scale); - updated = true; - - this.localScaling.setValue(this.prevScale.x, this.prevScale.y, this.prevScale.z); - this.compoundShape.setLocalScaling(this.localScaling); - } - - if (this.shapeComponentsChanged) { - this.shapeComponentsChanged = false; - updated = true; - for (let i = 0; i < this.shapeComponents.length; i++) { - const shapeComponent = this.shapeComponents[i]; - if (shapeComponent.getShapes().length === 0) { - this._createCollisionShape(shapeComponent); - } - const collisionShapes = shapeComponent.getShapes(); - for (let j = 0; j < collisionShapes.length; j++) { - const collisionShape = collisionShapes[j]; - if (!collisionShape.added) { - this.compoundShape.addChildShape(collisionShape.localTransform, collisionShape); - collisionShape.added = true; - } - } - } - - if (this.data.type === TYPE.DYNAMIC) { - this.updateMass(); - } - - this.system.driver.updateBody(this.body); - } - - //call initializePolyhedralFeatures for hull shapes if debug is turned on and/or scale changes - if (this.system.debug && (updated || !this.polyHedralFeaturesInitialized)) { - for (let i = 0; i < this.shapeComponents.length; i++) { - const collisionShapes = this.shapeComponents[i].getShapes(); - for (let j = 0; j < collisionShapes.length; j++) { - const collisionShape = collisionShapes[j]; - if (needsPolyhedralInitialization.indexOf(collisionShape.type) !== -1) { - collisionShape.initializePolyhedralFeatures(0); - } - } - } - this.polyHedralFeaturesInitialized = true; - } - }; - })(), - - _createCollisionShape: function(shapeComponent) { - const data = shapeComponent.data; - const vertices = []; - const matrices = []; - const indexes = []; - - const root = shapeComponent.el.object3D; - const matrixWorld = root.matrixWorld; - - threeToAmmo.iterateGeometries(root, data, (vertexArray, matrixArray, indexArray) => { - vertices.push(vertexArray); - matrices.push(matrixArray); - indexes.push(indexArray); - }); - - const collisionShapes = threeToAmmo.createCollisionShapes(vertices, matrices, indexes, matrixWorld.elements, data); - shapeComponent.addShapes(collisionShapes); - return; - }, - - /** - * Registers the component with the physics system. - */ - play: function() { - if (this.isLoaded) { - this._addToSystem(); - } - }, - - _addToSystem: function() { - if (!this.addedToSystem) { - this.system.addBody(this.body, this.data.collisionFilterGroup, this.data.collisionFilterMask); - - if (this.data.emitCollisionEvents) { - this.system.driver.addEventListener(this.body); - } - - this.system.addComponent(this); - this.addedToSystem = true; - } - }, - - /** - * Unregisters the component with the physics system. - */ - pause: function() { - if (this.addedToSystem) { - this.system.removeComponent(this); - this.system.removeBody(this.body); - this.addedToSystem = false; - } - }, - - /** - * Updates the rigid body instance, where possible. - */ - update: function(prevData) { - if (this.isLoaded) { - if (!this.hasUpdated) { - //skip the first update - this.hasUpdated = true; - return; - } - - const data = this.data; - - if (prevData.type !== data.type || prevData.disableCollision !== data.disableCollision) { - this.updateCollisionFlags(); - } - - if (prevData.activationState !== data.activationState) { - this.body.forceActivationState(ACTIVATION_STATES.indexOf(data.activationState) + 1); - if (data.activationState === ACTIVATION_STATE.ACTIVE_TAG) { - this.body.activate(true); - } - } - - if ( - prevData.collisionFilterGroup !== data.collisionFilterGroup || - prevData.collisionFilterMask !== data.collisionFilterMask - ) { - const broadphaseProxy = this.body.getBroadphaseProxy(); - broadphaseProxy.set_m_collisionFilterGroup(data.collisionFilterGroup); - broadphaseProxy.set_m_collisionFilterMask(data.collisionFilterMask); - this.system.driver.broadphase - .getOverlappingPairCache() - .removeOverlappingPairsContainingProxy(broadphaseProxy, this.system.driver.dispatcher); - } - - if (prevData.linearDamping != data.linearDamping || prevData.angularDamping != data.angularDamping) { - this.body.setDamping(data.linearDamping, data.angularDamping); - } - - if (!almostEqualsVector3(0.001, prevData.gravity, data.gravity)) { - this._updateBodyGravity(data.gravity) - } - - if ( - prevData.linearSleepingThreshold != data.linearSleepingThreshold || - prevData.angularSleepingThreshold != data.angularSleepingThreshold - ) { - this.body.setSleepingThresholds(data.linearSleepingThreshold, data.angularSleepingThreshold); - } - - if (!almostEqualsVector3(0.001, prevData.angularFactor, data.angularFactor)) { - const angularFactor = new Ammo.btVector3(data.angularFactor.x, data.angularFactor.y, data.angularFactor.z); - this.body.setAngularFactor(angularFactor); - Ammo.destroy(angularFactor); - } - - if (prevData.restitution != data.restitution ) { - console.warn("ammo-body restitution cannot be updated from its initial value.") - } - - //TODO: support dynamic update for other properties - } - }, - - /** - * Removes the component and all physics and scene side effects. - */ - remove: function() { - if (this.triMesh) Ammo.destroy(this.triMesh); - if (this.localScaling) Ammo.destroy(this.localScaling); - if (this.compoundShape) Ammo.destroy(this.compoundShape); - Ammo.destroy(this.rbInfo); - Ammo.destroy(this.msTransform); - Ammo.destroy(this.motionState); - Ammo.destroy(this.localInertia); - Ammo.destroy(this.rotation); - if (this.body) { - if (!this.data.emitCollisionEvents) { - // As per issue 47 / PR 48 there is a strange bug - // not yet understood, where destroying an Ammo body - // leads to subsequent issues reporting collision events. - // - // So if we are reporting collision events, preferable to - // tolerate a small memory leak, by not destroying the - // Ammo body, rather than missing collision events. - Ammo.destroy(this.body); - } - delete this.body; - } - }, - - beforeStep: function() { - this._updateShapes(); - // Note that since static objects don't move, - // we don't sync them to physics on a routine basis. - if (this.data.type === TYPE.KINEMATIC) { - this.syncToPhysics(); - } - }, - - step: function() { - if (this.data.type === TYPE.DYNAMIC) { - this.syncFromPhysics(); - } - }, - - /** - * Updates the rigid body's position, velocity, and rotation, based on the scene. - */ - syncToPhysics: (function() { - const q = new THREE.Quaternion(); - const v = new THREE.Vector3(); - const q2 = new THREE.Vector3(); - const v2 = new THREE.Vector3(); - return function() { - const el = this.el, - parentEl = el.parentEl, - body = this.body; - - if (!body) return; - - this.motionState.getWorldTransform(this.msTransform); - - if (parentEl.isScene) { - v.copy(el.object3D.position); - q.copy(el.object3D.quaternion); - } else { - el.object3D.getWorldPosition(v); - el.object3D.getWorldQuaternion(q); - } - - const position = this.msTransform.getOrigin(); - v2.set(position.x(), position.y(), position.z()); - - const quaternion = this.msTransform.getRotation(); - q2.set(quaternion.x(), quaternion.y(), quaternion.z(), quaternion.w()); - - if (!almostEqualsVector3(0.001, v, v2) || !almostEqualsQuaternion(0.001, q, q2)) { - if (!this.body.isActive()) { - this.body.activate(true); - } - this.msTransform.getOrigin().setValue(v.x, v.y, v.z); - this.rotation.setValue(q.x, q.y, q.z, q.w); - this.msTransform.setRotation(this.rotation); - this.motionState.setWorldTransform(this.msTransform); - - if (this.data.type !== TYPE.KINEMATIC) { - this.body.setCenterOfMassTransform(this.msTransform); - } - } - }; - })(), - - /** - * Updates the scene object's position and rotation, based on the physics simulation. - */ - syncFromPhysics: (function() { - const v = new THREE.Vector3(), - q1 = new THREE.Quaternion(), - q2 = new THREE.Quaternion(); - return function() { - this.motionState.getWorldTransform(this.msTransform); - const position = this.msTransform.getOrigin(); - const quaternion = this.msTransform.getRotation(); - - const el = this.el, - body = this.body; - - // For the parent, prefer to use the THHREE.js scene graph parent (if it can be determined) - // and only use the HTML scene graph parent as a fallback. - // Usually these are the same, but there are various cases where it's useful to modify the THREE.js - // scene graph so that it deviates from the HTML. - // In these cases the THREE.js scene graph should be considered the definitive reference in terms - // of object positioning etc. - // For specific examples, and more discussion, see: - // https://github.com/c-frame/aframe-physics-system/pull/1#issuecomment-1264686433 - const parentEl = el.object3D.parent.el ? el.object3D.parent.el : el.parentEl; - - if (!body) return; - if (!parentEl) return; - - if (parentEl.isScene) { - el.object3D.position.set(position.x(), position.y(), position.z()); - el.object3D.quaternion.set(quaternion.x(), quaternion.y(), quaternion.z(), quaternion.w()); - } else { - q1.set(quaternion.x(), quaternion.y(), quaternion.z(), quaternion.w()); - parentEl.object3D.getWorldQuaternion(q2); - q1.multiply(q2.invert()); - el.object3D.quaternion.copy(q1); - - v.set(position.x(), position.y(), position.z()); - parentEl.object3D.worldToLocal(v); - el.object3D.position.copy(v); - } - }; - })(), - - addShapeComponent: function(shapeComponent) { - if (shapeComponent.data.type === SHAPE.MESH && this.data.type !== TYPE.STATIC) { - console.warn("non-static mesh colliders not supported"); - return; - } - - this.shapeComponents.push(shapeComponent); - this.shapeComponentsChanged = true; - }, - - removeShapeComponent: function(shapeComponent) { - const index = this.shapeComponents.indexOf(shapeComponent); - if (this.compoundShape && index !== -1 && this.body) { - const shapes = shapeComponent.getShapes(); - for (var i = 0; i < shapes.length; i++) { - this.compoundShape.removeChildShape(shapes[i]); - } - this.shapeComponentsChanged = true; - this.shapeComponents.splice(index, 1); - } - }, - - updateMass: function() { - const shape = this.body.getCollisionShape(); - const mass = this.data.type === TYPE.DYNAMIC ? this.data.mass : 0; - shape.calculateLocalInertia(mass, this.localInertia); - this.body.setMassProps(mass, this.localInertia); - this.body.updateInertiaTensor(); - }, - - updateCollisionFlags: function() { - let flags = this.data.disableCollision ? 4 : 0; - switch (this.data.type) { - case TYPE.STATIC: - flags |= COLLISION_FLAG.STATIC_OBJECT; - break; - case TYPE.KINEMATIC: - flags |= COLLISION_FLAG.KINEMATIC_OBJECT; - break; - default: - this.body.applyGravity(); - break; - } - this.body.setCollisionFlags(flags); - - this.updateMass(); - - // TODO: enable CCD if dynamic? - // this.body.setCcdMotionThreshold(0.001); - // this.body.setCcdSweptSphereRadius(0.001); - - this.system.driver.updateBody(this.body); - }, - - getVelocity: function() { - return this.body.getLinearVelocity(); - } -}; - -module.exports.definition = AmmoBody; -module.exports.Component = AFRAME.registerComponent("ammo-body", AmmoBody); +/* global Ammo,THREE */ +const AmmoDebugDrawer = require("ammo-debug-drawer"); +const threeToAmmo = require("three-to-ammo"); +const CONSTANTS = require("../../constants"), + ACTIVATION_STATE = CONSTANTS.ACTIVATION_STATE, + COLLISION_FLAG = CONSTANTS.COLLISION_FLAG, + SHAPE = CONSTANTS.SHAPE, + TYPE = CONSTANTS.TYPE, + FIT = CONSTANTS.FIT; + +const ACTIVATION_STATES = [ + ACTIVATION_STATE.ACTIVE_TAG, + ACTIVATION_STATE.ISLAND_SLEEPING, + ACTIVATION_STATE.WANTS_DEACTIVATION, + ACTIVATION_STATE.DISABLE_DEACTIVATION, + ACTIVATION_STATE.DISABLE_SIMULATION +]; + +const RIGID_BODY_FLAGS = { + NONE: 0, + DISABLE_WORLD_GRAVITY: 1 +}; + +function almostEqualsVector3(epsilon, u, v) { + return Math.abs(u.x - v.x) < epsilon && Math.abs(u.y - v.y) < epsilon && Math.abs(u.z - v.z) < epsilon; +} + +function almostEqualsBtVector3(epsilon, u, v) { + return Math.abs(u.x() - v.x()) < epsilon && Math.abs(u.y() - v.y()) < epsilon && Math.abs(u.z() - v.z()) < epsilon; +} + +function almostEqualsQuaternion(epsilon, u, v) { + return ( + (Math.abs(u.x - v.x) < epsilon && + Math.abs(u.y - v.y) < epsilon && + Math.abs(u.z - v.z) < epsilon && + Math.abs(u.w - v.w) < epsilon) || + (Math.abs(u.x + v.x) < epsilon && + Math.abs(u.y + v.y) < epsilon && + Math.abs(u.z + v.z) < epsilon && + Math.abs(u.w + v.w) < epsilon) + ); +} + +let AmmoBody = { + schema: { + loadedEvent: { default: "" }, + mass: { default: 1 }, + gravity: { type: "vec3", default: null }, + linearDamping: { default: 0.01 }, + angularDamping: { default: 0.01 }, + linearSleepingThreshold: { default: 1.6 }, + angularSleepingThreshold: { default: 2.5 }, + angularFactor: { type: "vec3", default: { x: 1, y: 1, z: 1 } }, + activationState: { + default: ACTIVATION_STATE.ACTIVE_TAG, + oneOf: ACTIVATION_STATES + }, + type: { default: "dynamic", oneOf: [TYPE.STATIC, TYPE.DYNAMIC, TYPE.KINEMATIC] }, + emitCollisionEvents: { default: false }, + disableCollision: { default: false }, + collisionFilterGroup: { default: 1 }, //32-bit mask, + collisionFilterMask: { default: 1 }, //32-bit mask + scaleAutoUpdate: { default: true }, + restitution: {default: 0} // does not support updates + }, + + /** + * Initializes a body component, assigning it to the physics system and binding listeners for + * parsing the elements geometry. + */ + init: function() { + this.system = this.el.sceneEl.systems.physics; + this.shapeComponents = []; + + if (this.data.loadedEvent === "") { + this.loadedEventFired = true; + } else { + this.el.addEventListener( + this.data.loadedEvent, + () => { + this.loadedEventFired = true; + }, + { once: true } + ); + } + + if (this.system.initialized && this.loadedEventFired) { + this.initBody(); + } + }, + + /** + * Parses an element's geometry and component metadata to create an Ammo body instance for the + * component. + */ + initBody: (function() { + const pos = new THREE.Vector3(); + const quat = new THREE.Quaternion(); + const boundingBox = new THREE.Box3(); + + return function() { + const el = this.el, + data = this.data; + const clamp = (num, min, max) => Math.min(Math.max(num, min), max) + + this.localScaling = new Ammo.btVector3(); + + const obj = this.el.object3D; + obj.getWorldPosition(pos); + obj.getWorldQuaternion(quat); + + this.prevScale = new THREE.Vector3(1, 1, 1); + this.prevNumChildShapes = 0; + + this.msTransform = new Ammo.btTransform(); + this.msTransform.setIdentity(); + this.rotation = new Ammo.btQuaternion(quat.x, quat.y, quat.z, quat.w); + + this.msTransform.getOrigin().setValue(pos.x, pos.y, pos.z); + this.msTransform.setRotation(this.rotation); + + this.motionState = new Ammo.btDefaultMotionState(this.msTransform); + + this.localInertia = new Ammo.btVector3(0, 0, 0); + + this.compoundShape = new Ammo.btCompoundShape(true); + + this.rbInfo = new Ammo.btRigidBodyConstructionInfo( + data.mass, + this.motionState, + this.compoundShape, + this.localInertia + ); + this.rbInfo.m_restitution = clamp(this.data.restitution, 0, 1); + this.body = new Ammo.btRigidBody(this.rbInfo); + this.body.setActivationState(ACTIVATION_STATES.indexOf(data.activationState) + 1); + this.body.setSleepingThresholds(data.linearSleepingThreshold, data.angularSleepingThreshold); + + this.body.setDamping(data.linearDamping, data.angularDamping); + + const angularFactor = new Ammo.btVector3(data.angularFactor.x, data.angularFactor.y, data.angularFactor.z); + this.body.setAngularFactor(angularFactor); + Ammo.destroy(angularFactor); + + this._updateBodyGravity(data.gravity) + + this.updateCollisionFlags(); + + this.el.body = this.body; + this.body.el = el; + + this.isLoaded = true; + + this.el.emit("body-loaded", { body: this.el.body }); + + this._addToSystem(); + }; + })(), + + tick: function() { + if (this.system.initialized && !this.isLoaded && this.loadedEventFired) { + this.initBody(); + } + }, + + _updateBodyGravity(gravity) { + + if (gravity.x !== undefined && + gravity.y !== undefined && + gravity.z !== undefined) { + const gravityBtVec = new Ammo.btVector3(gravity.x, gravity.y, gravity.z); + if (!almostEqualsBtVector3(0.001, gravityBtVec, this.system.driver.physicsWorld.getGravity())) { + this.body.setFlags(RIGID_BODY_FLAGS.DISABLE_WORLD_GRAVITY); + } else { + this.body.setFlags(RIGID_BODY_FLAGS.NONE); + } + this.body.setGravity(gravityBtVec); + Ammo.destroy(gravityBtVec); + } + else { + // no per-body gravity specified - just use world gravity + this.body.setFlags(RIGID_BODY_FLAGS.NONE); + } + }, + + _updateShapes: (function() { + const needsPolyhedralInitialization = [SHAPE.HULL, SHAPE.HACD, SHAPE.VHACD]; + return function() { + let updated = false; + + const obj = this.el.object3D; + if (this.data.scaleAutoUpdate && this.prevScale && !almostEqualsVector3(0.001, obj.scale, this.prevScale)) { + this.prevScale.copy(obj.scale); + updated = true; + + this.localScaling.setValue(this.prevScale.x, this.prevScale.y, this.prevScale.z); + this.compoundShape.setLocalScaling(this.localScaling); + } + + if (this.shapeComponentsChanged) { + this.shapeComponentsChanged = false; + updated = true; + for (let i = 0; i < this.shapeComponents.length; i++) { + const shapeComponent = this.shapeComponents[i]; + if (shapeComponent.getShapes().length === 0) { + this._createCollisionShape(shapeComponent); + } + const collisionShapes = shapeComponent.getShapes(); + for (let j = 0; j < collisionShapes.length; j++) { + const collisionShape = collisionShapes[j]; + if (!collisionShape.added) { + this.compoundShape.addChildShape(collisionShape.localTransform, collisionShape); + collisionShape.added = true; + } + } + } + + if (this.data.type === TYPE.DYNAMIC) { + this.updateMass(); + } + + this.system.driver.updateBody(this.body); + } + + //call initializePolyhedralFeatures for hull shapes if debug is turned on and/or scale changes + if (this.system.debug && (updated || !this.polyHedralFeaturesInitialized)) { + for (let i = 0; i < this.shapeComponents.length; i++) { + const collisionShapes = this.shapeComponents[i].getShapes(); + for (let j = 0; j < collisionShapes.length; j++) { + const collisionShape = collisionShapes[j]; + if (needsPolyhedralInitialization.indexOf(collisionShape.type) !== -1) { + collisionShape.initializePolyhedralFeatures(0); + } + } + } + this.polyHedralFeaturesInitialized = true; + } + }; + })(), + + _createCollisionShape: function(shapeComponent) { + const data = shapeComponent.data; + const vertices = []; + const matrices = []; + const indexes = []; + + const root = shapeComponent.el.object3D; + const matrixWorld = root.matrixWorld; + + threeToAmmo.iterateGeometries(root, data, (vertexArray, matrixArray, indexArray) => { + vertices.push(vertexArray); + matrices.push(matrixArray); + indexes.push(indexArray); + }); + + const collisionShapes = threeToAmmo.createCollisionShapes(vertices, matrices, indexes, matrixWorld.elements, data); + shapeComponent.addShapes(collisionShapes); + return; + }, + + /** + * Registers the component with the physics system. + */ + play: function() { + if (this.isLoaded) { + this._addToSystem(); + } + }, + + _addToSystem: function() { + if (!this.addedToSystem) { + this.system.addBody(this.body, this.data.collisionFilterGroup, this.data.collisionFilterMask); + + if (this.data.emitCollisionEvents) { + this.system.driver.addEventListener(this.body); + } + + this.system.addComponent(this); + this.addedToSystem = true; + } + }, + + /** + * Unregisters the component with the physics system. + */ + pause: function() { + if (this.addedToSystem) { + this.system.removeComponent(this); + this.system.removeBody(this.body); + this.addedToSystem = false; + } + }, + + /** + * Updates the rigid body instance, where possible. + */ + update: function(prevData) { + if (this.isLoaded) { + if (!this.hasUpdated) { + //skip the first update + this.hasUpdated = true; + return; + } + + const data = this.data; + + if (prevData.type !== data.type || prevData.disableCollision !== data.disableCollision) { + this.updateCollisionFlags(); + } + + if (prevData.activationState !== data.activationState) { + this.body.forceActivationState(ACTIVATION_STATES.indexOf(data.activationState) + 1); + if (data.activationState === ACTIVATION_STATE.ACTIVE_TAG) { + this.body.activate(true); + } + } + + if ( + prevData.collisionFilterGroup !== data.collisionFilterGroup || + prevData.collisionFilterMask !== data.collisionFilterMask + ) { + const broadphaseProxy = this.body.getBroadphaseProxy(); + broadphaseProxy.set_m_collisionFilterGroup(data.collisionFilterGroup); + broadphaseProxy.set_m_collisionFilterMask(data.collisionFilterMask); + this.system.driver.broadphase + .getOverlappingPairCache() + .removeOverlappingPairsContainingProxy(broadphaseProxy, this.system.driver.dispatcher); + } + + if (prevData.linearDamping != data.linearDamping || prevData.angularDamping != data.angularDamping) { + this.body.setDamping(data.linearDamping, data.angularDamping); + } + + if (!almostEqualsVector3(0.001, prevData.gravity, data.gravity)) { + this._updateBodyGravity(data.gravity) + } + + if ( + prevData.linearSleepingThreshold != data.linearSleepingThreshold || + prevData.angularSleepingThreshold != data.angularSleepingThreshold + ) { + this.body.setSleepingThresholds(data.linearSleepingThreshold, data.angularSleepingThreshold); + } + + if (!almostEqualsVector3(0.001, prevData.angularFactor, data.angularFactor)) { + const angularFactor = new Ammo.btVector3(data.angularFactor.x, data.angularFactor.y, data.angularFactor.z); + this.body.setAngularFactor(angularFactor); + Ammo.destroy(angularFactor); + } + + if (prevData.restitution != data.restitution ) { + console.warn("ammo-body restitution cannot be updated from its initial value.") + } + + //TODO: support dynamic update for other properties + } + }, + + /** + * Removes the component and all physics and scene side effects. + */ + remove: function() { + if (this.triMesh) Ammo.destroy(this.triMesh); + if (this.localScaling) Ammo.destroy(this.localScaling); + if (this.compoundShape) Ammo.destroy(this.compoundShape); + if (this.body) { + Ammo.destroy(this.body); + delete this.body; + } + Ammo.destroy(this.rbInfo); + Ammo.destroy(this.msTransform); + Ammo.destroy(this.motionState); + Ammo.destroy(this.localInertia); + Ammo.destroy(this.rotation); + }, + + beforeStep: function() { + this._updateShapes(); + // Note that since static objects don't move, + // we don't sync them to physics on a routine basis. + if (this.data.type === TYPE.KINEMATIC) { + this.syncToPhysics(); + } + }, + + step: function() { + if (this.data.type === TYPE.DYNAMIC) { + this.syncFromPhysics(); + } + }, + + /** + * Updates the rigid body's position, velocity, and rotation, based on the scene. + */ + syncToPhysics: (function() { + const q = new THREE.Quaternion(); + const v = new THREE.Vector3(); + const q2 = new THREE.Vector3(); + const v2 = new THREE.Vector3(); + return function() { + const el = this.el, + parentEl = el.parentEl, + body = this.body; + + if (!body) return; + + this.motionState.getWorldTransform(this.msTransform); + + if (parentEl.isScene) { + v.copy(el.object3D.position); + q.copy(el.object3D.quaternion); + } else { + el.object3D.getWorldPosition(v); + el.object3D.getWorldQuaternion(q); + } + + const position = this.msTransform.getOrigin(); + v2.set(position.x(), position.y(), position.z()); + + const quaternion = this.msTransform.getRotation(); + q2.set(quaternion.x(), quaternion.y(), quaternion.z(), quaternion.w()); + + if (!almostEqualsVector3(0.001, v, v2) || !almostEqualsQuaternion(0.001, q, q2)) { + if (!this.body.isActive()) { + this.body.activate(true); + } + this.msTransform.getOrigin().setValue(v.x, v.y, v.z); + this.rotation.setValue(q.x, q.y, q.z, q.w); + this.msTransform.setRotation(this.rotation); + this.motionState.setWorldTransform(this.msTransform); + + if (this.data.type !== TYPE.KINEMATIC) { + this.body.setCenterOfMassTransform(this.msTransform); + } + } + }; + })(), + + /** + * Updates the scene object's position and rotation, based on the physics simulation. + */ + syncFromPhysics: (function() { + const v = new THREE.Vector3(), + q1 = new THREE.Quaternion(), + q2 = new THREE.Quaternion(); + return function() { + this.motionState.getWorldTransform(this.msTransform); + const position = this.msTransform.getOrigin(); + const quaternion = this.msTransform.getRotation(); + + const el = this.el, + body = this.body; + + // For the parent, prefer to use the THHREE.js scene graph parent (if it can be determined) + // and only use the HTML scene graph parent as a fallback. + // Usually these are the same, but there are various cases where it's useful to modify the THREE.js + // scene graph so that it deviates from the HTML. + // In these cases the THREE.js scene graph should be considered the definitive reference in terms + // of object positioning etc. + // For specific examples, and more discussion, see: + // https://github.com/c-frame/aframe-physics-system/pull/1#issuecomment-1264686433 + const parentEl = el.object3D.parent.el ? el.object3D.parent.el : el.parentEl; + + if (!body) return; + if (!parentEl) return; + + if (parentEl.isScene) { + el.object3D.position.set(position.x(), position.y(), position.z()); + el.object3D.quaternion.set(quaternion.x(), quaternion.y(), quaternion.z(), quaternion.w()); + } else { + q1.set(quaternion.x(), quaternion.y(), quaternion.z(), quaternion.w()); + parentEl.object3D.getWorldQuaternion(q2); + q1.multiply(q2.invert()); + el.object3D.quaternion.copy(q1); + + v.set(position.x(), position.y(), position.z()); + parentEl.object3D.worldToLocal(v); + el.object3D.position.copy(v); + } + }; + })(), + + addShapeComponent: function(shapeComponent) { + if (shapeComponent.data.type === SHAPE.MESH && this.data.type !== TYPE.STATIC) { + console.warn("non-static mesh colliders not supported"); + return; + } + + this.shapeComponents.push(shapeComponent); + this.shapeComponentsChanged = true; + }, + + removeShapeComponent: function(shapeComponent) { + const index = this.shapeComponents.indexOf(shapeComponent); + if (this.compoundShape && index !== -1 && this.body) { + const shapes = shapeComponent.getShapes(); + for (var i = 0; i < shapes.length; i++) { + this.compoundShape.removeChildShape(shapes[i]); + } + this.shapeComponentsChanged = true; + this.shapeComponents.splice(index, 1); + } + }, + + updateMass: function() { + const shape = this.body.getCollisionShape(); + const mass = this.data.type === TYPE.DYNAMIC ? this.data.mass : 0; + shape.calculateLocalInertia(mass, this.localInertia); + this.body.setMassProps(mass, this.localInertia); + this.body.updateInertiaTensor(); + }, + + updateCollisionFlags: function() { + let flags = this.data.disableCollision ? 4 : 0; + switch (this.data.type) { + case TYPE.STATIC: + flags |= COLLISION_FLAG.STATIC_OBJECT; + break; + case TYPE.KINEMATIC: + flags |= COLLISION_FLAG.KINEMATIC_OBJECT; + break; + default: + this.body.applyGravity(); + break; + } + this.body.setCollisionFlags(flags); + + this.updateMass(); + + // TODO: enable CCD if dynamic? + // this.body.setCcdMotionThreshold(0.001); + // this.body.setCcdSweptSphereRadius(0.001); + + this.system.driver.updateBody(this.body); + }, + + getVelocity: function() { + return this.body.getLinearVelocity(); + } +}; + +module.exports.definition = AmmoBody; +module.exports.Component = AFRAME.registerComponent("ammo-body", AmmoBody); + +},{"../../constants":20,"ammo-debug-drawer":4,"three-to-ammo":6}],11:[function(require,module,exports){ +var CANNON = require('cannon-es'); +const { threeToCannon, ShapeType } = require('three-to-cannon'); +const identityQuaternion = new THREE.Quaternion() + +function mesh2shape (object, options) { + + const result = threeToCannon(object, options); + return result; +} + +require('../../../lib/CANNON-shape2mesh'); + +var Body = { + dependencies: ['velocity'], + + schema: { + mass: {default: 5, if: {type: 'dynamic'}}, + linearDamping: { default: 0.01, if: {type: 'dynamic'}}, + angularDamping: { default: 0.01, if: {type: 'dynamic'}}, + shape: {default: 'auto', oneOf: ['auto', 'box', 'cylinder', 'sphere', 'hull', 'mesh', 'none']}, + cylinderAxis: {default: 'y', oneOf: ['x', 'y', 'z']}, + sphereRadius: {default: NaN}, + type: {default: 'dynamic', oneOf: ['static', 'dynamic']} + }, + + /** + * Initializes a body component, assigning it to the physics system and binding listeners for + * parsing the elements geometry. + */ + init: function () { + this.system = this.el.sceneEl.systems.physics; + + if (this.el.sceneEl.hasLoaded) { + this.initBody(); + } else { + this.el.sceneEl.addEventListener('loaded', this.initBody.bind(this)); + } + }, + + /** + * Parses an element's geometry and component metadata to create a CANNON.Body instance for the + * component. + */ + initBody: function () { + var el = this.el, + data = this.data; + + var obj = this.el.object3D; + var pos = obj.position; + var quat = obj.quaternion; + + this.body = new CANNON.Body({ + mass: data.type === 'static' ? 0 : data.mass || 0, + material: this.system.getMaterial('defaultMaterial'), + position: new CANNON.Vec3(pos.x, pos.y, pos.z), + quaternion: new CANNON.Quaternion(quat.x, quat.y, quat.z, quat.w), + linearDamping: data.linearDamping, + angularDamping: data.angularDamping, + type: data.type === 'dynamic' ? CANNON.Body.DYNAMIC : CANNON.Body.STATIC, + }); + + // Matrix World must be updated at root level, if scale is to be applied – updateMatrixWorld() + // only checks an object's parent, not the rest of the ancestors. Hence, a wrapping entity with + // scale="0.5 0.5 0.5" will be ignored. + // Reference: https://github.com/mrdoob/three.js/blob/master/src/core/Object3D.js#L511-L541 + // Potential fix: https://github.com/mrdoob/three.js/pull/7019 + this.el.object3D.updateMatrixWorld(true); + + if(data.shape !== 'none') { + var options = data.shape === 'auto' ? undefined : AFRAME.utils.extend({}, this.data, { + type: ShapeType[data.shape.toUpperCase()] + }); + + const shapeInfo = mesh2shape(this.el.object3D, options); + let shape, offset, orientation; + if (shapeInfo) { + ({ shape, offset, orientation } = shapeInfo); + } + + if (!shape) { + el.addEventListener('object3dset', this.initBody.bind(this)); + return; + } + + this.body.addShape(shape, offset, orientation); + + // Show wireframe + if (this.system.debug) { + this.shouldUpdateWireframe = true; + } + + this.hasShape = true; + } + + this.el.body = this.body; + this.body.el = el; + + // If component wasn't initialized when play() was called, finish up. + if (this.isPlaying) { + this._play(); + } + + if (this.hasShape) { + this.el.emit('body-loaded', {body: this.el.body}); + } + }, + + addShape: function(shape, offset, orientation) { + if (this.data.shape !== 'none') { + console.warn('shape can only be added if shape property is none'); + return; + } + + if (!shape) { + console.warn('shape cannot be null'); + return; + } + + if (!this.body) { + console.warn('shape cannot be added before body is loaded'); + return; + } + this.body.addShape(shape, offset, orientation); + + if (this.system.debug) { + this.shouldUpdateWireframe = true; + } + + this.shouldUpdateBody = true; + }, + + tick: function () { + if (this.shouldUpdateBody) { + + // Calling play will result in the object being re-added to the + // physics system with the updated body / shape data. + // But we mustn't add it twice, so any previously loaded body should be paused first. + this._pause(); + this.hasShape = true; + this._play() + + this.el.emit('body-loaded', {body: this.el.body}); + this.shouldUpdateBody = false; + } + + if (this.shouldUpdateWireframe) { + this.createWireframe(this.body); + this.shouldUpdateWireframe = false; + } + }, + + /** + * Registers the component with the physics system, if ready. + */ + play: function () { + this._play(); + }, + + /** + * Internal helper to register component with physics system. + */ + _play: function () { + + if (!this.hasShape) return; + + this.syncToPhysics(); + this.system.addComponent(this); + this.system.addBody(this.body); + if (this.wireframe) this.el.sceneEl.object3D.add(this.wireframe); + }, + + /** + * Unregisters the component with the physics system. + */ + pause: function () { + this._pause(); + }, + + _pause: function () { + + if (!this.hasShape) return; + + this.system.removeComponent(this); + if (this.body) this.system.removeBody(this.body); + if (this.wireframe) this.el.sceneEl.object3D.remove(this.wireframe); + }, + + /** + * Updates the CANNON.Body instance, where possible. + */ + update: function (prevData) { + if (!this.body) return; + + var data = this.data; + + if (prevData.type != undefined && data.type != prevData.type) { + this.body.type = data.type === 'dynamic' ? CANNON.Body.DYNAMIC : CANNON.Body.STATIC; + } + + this.body.mass = data.mass || 0; + if (data.type === 'dynamic') { + this.body.linearDamping = data.linearDamping; + this.body.angularDamping = data.angularDamping; + } + if (data.mass !== prevData.mass) { + this.body.updateMassProperties(); + } + if (this.body.updateProperties) this.body.updateProperties(); + }, + + /** + * Removes the component and all physics and scene side effects. + */ + remove: function () { + if (this.body) { + delete this.body.el; + delete this.body; + } + delete this.el.body; + delete this.wireframe; + }, + + beforeStep: function () { + if (this.body.mass === 0) { + this.syncToPhysics(); + } + }, + + step: function () { + if (this.body.mass !== 0) { + this.syncFromPhysics(); + } + }, + + /** + * Creates a wireframe for the body, for debugging. + * TODO(donmccurdy) – Refactor this into a standalone utility or component. + * @param {CANNON.Body} body + * @param {CANNON.Shape} shape + */ + createWireframe: function (body) { + if (this.wireframe) { + this.el.sceneEl.object3D.remove(this.wireframe); + delete this.wireframe; + } + this.wireframe = new THREE.Object3D(); + this.el.sceneEl.object3D.add(this.wireframe); + + var offset, mesh; + var orientation = new THREE.Quaternion(); + for (var i = 0; i < this.body.shapes.length; i++) + { + offset = this.body.shapeOffsets[i], + orientation.copy(this.body.shapeOrientations[i]), + mesh = CANNON.shape2mesh(this.body).children[i]; + + var wireframe = new THREE.LineSegments( + new THREE.EdgesGeometry(mesh.geometry), + new THREE.LineBasicMaterial({color: 0xff0000}) + ); + + if (offset) { + wireframe.position.copy(offset); + } + + if (orientation) { + wireframe.quaternion.copy(orientation); + } + + this.wireframe.add(wireframe); + } + + this.syncWireframe(); + }, + + /** + * Updates the debugging wireframe's position and rotation. + */ + syncWireframe: function () { + var offset, + wireframe = this.wireframe; + + if (!this.wireframe) return; + + // Apply rotation. If the shape required custom orientation, also apply + // that on the wireframe. + wireframe.quaternion.copy(this.body.quaternion); + if (wireframe.orientation) { + wireframe.quaternion.multiply(wireframe.orientation); + } + + // Apply position. If the shape required custom offset, also apply that on + // the wireframe. + wireframe.position.copy(this.body.position); + if (wireframe.offset) { + offset = wireframe.offset.clone().applyQuaternion(wireframe.quaternion); + wireframe.position.add(offset); + } + + wireframe.updateMatrix(); + }, + + /** + * Updates the CANNON.Body instance's position, velocity, and rotation, based on the scene. + */ + syncToPhysics: (function () { + var q = new THREE.Quaternion(), + v = new THREE.Vector3(); + return function () { + var el = this.el, + parentEl = el.parentEl, + body = this.body; + + if (!body) return; + + if (el.components.velocity) body.velocity.copy(el.getAttribute('velocity')); + + if (parentEl.isScene) { + body.quaternion.copy(el.object3D.quaternion); + body.position.copy(el.object3D.position); + } else { + el.object3D.getWorldQuaternion(q); + body.quaternion.copy(q); + el.object3D.getWorldPosition(v); + body.position.copy(v); + } + + if (this.body.updateProperties) this.body.updateProperties(); + if (this.wireframe) this.syncWireframe(); + }; + }()), + + /** + * Updates the scene object's position and rotation, based on the physics simulation. + */ + syncFromPhysics: (function () { + var v = new THREE.Vector3(), + q1 = new THREE.Quaternion(), + q2 = new THREE.Quaternion(); + return function () { + var el = this.el, + parentEl = el.parentEl, + body = this.body; + + if (!body) return; + if (!parentEl) return; + + if (parentEl.isScene) { + el.object3D.quaternion.copy(body.quaternion); + el.object3D.position.copy(body.position); + } else { + q1.copy(body.quaternion); + parentEl.object3D.getWorldQuaternion(q2); + q1.premultiply(q2.invert()); + el.object3D.quaternion.copy(q1); + + v.copy(body.position); + parentEl.object3D.worldToLocal(v); + el.object3D.position.copy(v); + } + + if (this.wireframe) this.syncWireframe(); + }; + }()) +}; + +module.exports.definition = Body; +module.exports.Component = AFRAME.registerComponent('body', Body); + +},{"../../../lib/CANNON-shape2mesh":2,"cannon-es":5,"three-to-cannon":7}],12:[function(require,module,exports){ +var Body = require('./body'); + +/** + * Dynamic body. + * + * Moves according to physics simulation, and may collide with other objects. + */ +var DynamicBody = AFRAME.utils.extend({}, Body.definition); + +module.exports = AFRAME.registerComponent('dynamic-body', DynamicBody); + +},{"./body":11}],13:[function(require,module,exports){ +var Body = require('./body'); + +/** + * Static body. + * + * Solid body with a fixed position. Unaffected by gravity and collisions, but + * other objects may collide with it. + */ +var StaticBody = AFRAME.utils.extend({}, Body.definition); + +StaticBody.schema = AFRAME.utils.extend({}, Body.definition.schema, { + type: {default: 'static', oneOf: ['static', 'dynamic']}, + mass: {default: 0} +}); + +module.exports = AFRAME.registerComponent('static-body', StaticBody); + +},{"./body":11}],14:[function(require,module,exports){ +var CANNON = require("cannon-es"); + +module.exports = AFRAME.registerComponent("constraint", { + multiple: true, + + schema: { + // Type of constraint. + type: { default: "lock", oneOf: ["coneTwist", "distance", "hinge", "lock", "pointToPoint"] }, + + // Target (other) body for the constraint. + target: { type: "selector" }, + + // Maximum force that should be applied to constraint the bodies. + maxForce: { default: 1e6, min: 0 }, + + // If true, bodies can collide when they are connected. + collideConnected: { default: true }, + + // Wake up bodies when connected. + wakeUpBodies: { default: true }, + + // The distance to be kept between the bodies. If 0, will be set to current distance. + distance: { default: 0, min: 0 }, + + // Offset of the hinge or point-to-point constraint, defined locally in the body. + pivot: { type: "vec3" }, + targetPivot: { type: "vec3" }, + + // An axis that each body can rotate around, defined locally to that body. + axis: { type: "vec3", default: { x: 0, y: 0, z: 1 } }, + targetAxis: { type: "vec3", default: { x: 0, y: 0, z: 1 } } + }, + + init: function() { + this.system = this.el.sceneEl.systems.physics; + this.constraint = /* {CANNON.Constraint} */ null; + }, + + remove: function() { + if (!this.constraint) return; + + this.system.removeConstraint(this.constraint); + this.constraint = null; + }, + + update: function() { + var el = this.el, + data = this.data; + + this.remove(); + + if (!el.body || !data.target.body) { + (el.body ? data.target : el).addEventListener("body-loaded", this.update.bind(this, {})); + return; + } + + this.constraint = this.createConstraint(); + this.system.addConstraint(this.constraint); + }, + + /** + * Creates a new constraint, given current component data. The CANNON.js constructors are a bit + * different for each constraint type. A `.type` property is added to each constraint, because + * `instanceof` checks are not reliable for some types. These types are needed for serialization. + * @return {CANNON.Constraint} + */ + createConstraint: function() { + var constraint, + data = this.data, + pivot = new CANNON.Vec3(data.pivot.x, data.pivot.y, data.pivot.z), + targetPivot = new CANNON.Vec3(data.targetPivot.x, data.targetPivot.y, data.targetPivot.z), + axis = new CANNON.Vec3(data.axis.x, data.axis.y, data.axis.z), + targetAxis = new CANNON.Vec3(data.targetAxis.x, data.targetAxis.y, data.targetAxis.z); + + var constraint; + + switch (data.type) { + case "lock": + constraint = new CANNON.LockConstraint(this.el.body, data.target.body, { maxForce: data.maxForce }); + constraint.type = "LockConstraint"; + break; + + case "distance": + constraint = new CANNON.DistanceConstraint(this.el.body, data.target.body, data.distance, data.maxForce); + constraint.type = "DistanceConstraint"; + break; + + case "hinge": + constraint = new CANNON.HingeConstraint(this.el.body, data.target.body, { + pivotA: pivot, + pivotB: targetPivot, + axisA: axis, + axisB: targetAxis, + maxForce: data.maxForce + }); + constraint.type = "HingeConstraint"; + break; + + case "coneTwist": + constraint = new CANNON.ConeTwistConstraint(this.el.body, data.target.body, { + pivotA: pivot, + pivotB: targetPivot, + axisA: axis, + axisB: targetAxis, + maxForce: data.maxForce + }); + constraint.type = "ConeTwistConstraint"; + break; + + case "pointToPoint": + constraint = new CANNON.PointToPointConstraint( + this.el.body, + pivot, + data.target.body, + targetPivot, + data.maxForce + ); + constraint.type = "PointToPointConstraint"; + break; + + default: + throw new Error("[constraint] Unexpected type: " + data.type); + } + + constraint.collideConnected = data.collideConnected; + return constraint; + } +}); + +},{"cannon-es":5}],15:[function(require,module,exports){ +module.exports = { + 'velocity': require('./velocity'), + + registerAll: function (AFRAME) { + if (this._registered) return; + + AFRAME = AFRAME || window.AFRAME; + + if (!AFRAME.components['velocity']) AFRAME.registerComponent('velocity', this.velocity); + + this._registered = true; + } +}; + +},{"./velocity":16}],16:[function(require,module,exports){ +/** + * Velocity, in m/s. + */ +module.exports = AFRAME.registerComponent('velocity', { + schema: {type: 'vec3'}, + + init: function () { + this.system = this.el.sceneEl.systems.physics; + + if (this.system) { + this.system.addComponent(this); + } + }, + + remove: function () { + if (this.system) { + this.system.removeComponent(this); + } + }, + + tick: function (t, dt) { + if (!dt) return; + if (this.system) return; + this.afterStep(t, dt); + }, + + afterStep: function (t, dt) { + if (!dt) return; + + var physics = this.el.sceneEl.systems.physics || {data: {maxInterval: 1 / 60}}, + + // TODO - There's definitely a bug with getComputedAttribute and el.data. + velocity = this.el.getAttribute('velocity') || {x: 0, y: 0, z: 0}, + position = this.el.object3D.position || {x: 0, y: 0, z: 0}; + + dt = Math.min(dt, physics.data.maxInterval * 1000); + + this.el.object3D.position.set( + position.x + velocity.x * dt / 1000, + position.y + velocity.y * dt / 1000, + position.z + velocity.z * dt / 1000 + ); + } +}); + +},{}],17:[function(require,module,exports){ +/* global Ammo,THREE */ +const threeToAmmo = require("three-to-ammo"); +const CONSTANTS = require("../../constants"), + SHAPE = CONSTANTS.SHAPE, + FIT = CONSTANTS.FIT; + +var AmmoShape = { + schema: { + type: { + default: SHAPE.HULL, + oneOf: [ + SHAPE.BOX, + SHAPE.CYLINDER, + SHAPE.SPHERE, + SHAPE.CAPSULE, + SHAPE.CONE, + SHAPE.HULL, + SHAPE.HACD, + SHAPE.VHACD, + SHAPE.MESH, + SHAPE.HEIGHTFIELD + ] + }, + fit: { default: FIT.ALL, oneOf: [FIT.ALL, FIT.MANUAL] }, + halfExtents: { type: "vec3", default: { x: 1, y: 1, z: 1 } }, + minHalfExtent: { default: 0 }, + maxHalfExtent: { default: Number.POSITIVE_INFINITY }, + sphereRadius: { default: NaN }, + cylinderAxis: { default: "y", oneOf: ["x", "y", "z"] }, + margin: { default: 0.01 }, + offset: { type: "vec3", default: { x: 0, y: 0, z: 0 } }, + orientation: { type: "vec4", default: { x: 0, y: 0, z: 0, w: 1 } }, + heightfieldData: { default: [] }, + heightfieldDistance: { default: 1 }, + includeInvisible: { default: false } + }, + + multiple: true, + + init: function() { + if (this.data.fit !== FIT.MANUAL) { + if (this.el.object3DMap.mesh) { + this.mesh = this.el.object3DMap.mesh; + } else { + const self = this; + this.el.addEventListener("object3dset", function (e) { + if (e.detail.type === "mesh") { + self.init(); + } + }); + console.log("Cannot use FIT.ALL without object3DMap.mesh. Waiting for it to be set."); + return; + } + } + + this.system = this.el.sceneEl.systems.physics; + this.collisionShapes = []; + + let bodyEl = this.el; + this.body = bodyEl.components["ammo-body"] || null; + while (!this.body && bodyEl.parentNode != this.el.sceneEl) { + bodyEl = bodyEl.parentNode; + if (bodyEl.components["ammo-body"]) { + this.body = bodyEl.components["ammo-body"]; + } + } + if (!this.body) { + console.warn("body not found"); + return; + } + this.body.addShapeComponent(this); + }, + + getMesh: function() { + return this.mesh || null; + }, + + addShapes: function(collisionShapes) { + this.collisionShapes = collisionShapes; + }, + + getShapes: function() { + return this.collisionShapes; + }, + + remove: function() { + if (!this.body) { + return; + } + + this.body.removeShapeComponent(this); + + while (this.collisionShapes.length > 0) { + const collisionShape = this.collisionShapes.pop(); + collisionShape.destroy(); + Ammo.destroy(collisionShape.localTransform); + } + } +}; + +module.exports.definition = AmmoShape; +module.exports.Component = AFRAME.registerComponent("ammo-shape", AmmoShape); + +},{"../../constants":20,"three-to-ammo":6}],18:[function(require,module,exports){ +var CANNON = require('cannon-es'); + +var Shape = { + schema: { + shape: {default: 'box', oneOf: ['box', 'sphere', 'cylinder']}, + offset: {type: 'vec3', default: {x: 0, y: 0, z: 0}}, + orientation: {type: 'vec4', default: {x: 0, y: 0, z: 0, w: 1}}, + + // sphere + radius: {type: 'number', default: 1, if: {shape: ['sphere']}}, + + // box + halfExtents: {type: 'vec3', default: {x: 0.5, y: 0.5, z: 0.5}, if: {shape: ['box']}}, + + // cylinder + radiusTop: {type: 'number', default: 1, if: {shape: ['cylinder']}}, + radiusBottom: {type: 'number', default: 1, if: {shape: ['cylinder']}}, + height: {type: 'number', default: 1, if: {shape: ['cylinder']}}, + numSegments: {type: 'int', default: 8, if: {shape: ['cylinder']}} + }, + + multiple: true, + + init: function() { + if (this.el.sceneEl.hasLoaded) { + this.initShape(); + } else { + this.el.sceneEl.addEventListener('loaded', this.initShape.bind(this)); + } + }, + + initShape: function() { + this.bodyEl = this.el; + var bodyType = this._findType(this.bodyEl); + var data = this.data; + + while (!bodyType && this.bodyEl.parentNode != this.el.sceneEl) { + this.bodyEl = this.bodyEl.parentNode; + bodyType = this._findType(this.bodyEl); + } + + if (!bodyType) { + console.warn('body not found'); + return; + } + + var scale = new THREE.Vector3(); + this.bodyEl.object3D.getWorldScale(scale); + var shape, offset, orientation; + + if (data.hasOwnProperty('offset')) { + offset = new CANNON.Vec3( + data.offset.x * scale.x, + data.offset.y * scale.y, + data.offset.z * scale.z + ); + } + + if (data.hasOwnProperty('orientation')) { + orientation = new CANNON.Quaternion(); + orientation.copy(data.orientation); + } + + switch(data.shape) { + case 'sphere': + shape = new CANNON.Sphere(data.radius * scale.x); + break; + case 'box': + var halfExtents = new CANNON.Vec3( + data.halfExtents.x * scale.x, + data.halfExtents.y * scale.y, + data.halfExtents.z * scale.z + ); + shape = new CANNON.Box(halfExtents); + break; + case 'cylinder': + shape = new CANNON.Cylinder( + data.radiusTop * scale.x, + data.radiusBottom * scale.x, + data.height * scale.y, + data.numSegments + ); + + //rotate by 90 degrees similar to mesh2shape:createCylinderShape + var quat = new CANNON.Quaternion(); + quat.setFromEuler(90 * THREE.MathUtils.DEG2RAD, 0, 0, 'XYZ').normalize(); + orientation.mult(quat, orientation); + break; + default: + console.warn(data.shape + ' shape not supported'); + return; + } + + if (this.bodyEl.body) { + this.bodyEl.components[bodyType].addShape(shape, offset, orientation); + } else { + this.bodyEl.addEventListener('body-loaded', function() { + this.bodyEl.components[bodyType].addShape(shape, offset, orientation); + }, {once: true}); + } + }, + + _findType: function(el) { + if (el.hasAttribute('body')) { + return 'body'; + } else if (el.hasAttribute('dynamic-body')) { + return 'dynamic-body'; + } else if (el.hasAttribute('static-body')) { + return'static-body'; + } + return null; + }, + + remove: function() { + if (this.bodyEl.parentNode) { + console.warn('removing shape component not currently supported'); + } + } +}; + +module.exports.definition = Shape; +module.exports.Component = AFRAME.registerComponent('shape', Shape); + +},{"cannon-es":5}],19:[function(require,module,exports){ +var CANNON = require('cannon-es'); + +module.exports = AFRAME.registerComponent('spring', { + + multiple: true, + + schema: { + // Target (other) body for the constraint. + target: {type: 'selector'}, + + // Length of the spring, when no force acts upon it. + restLength: {default: 1, min: 0}, + + // How much will the spring suppress the force. + stiffness: {default: 100, min: 0}, + + // Stretch factor of the spring. + damping: {default: 1, min: 0}, + + // Offsets. + localAnchorA: {type: 'vec3', default: {x: 0, y: 0, z: 0}}, + localAnchorB: {type: 'vec3', default: {x: 0, y: 0, z: 0}}, + }, + + init: function() { + this.system = this.el.sceneEl.systems.physics; + this.system.addComponent(this); + this.isActive = true; + this.spring = /* {CANNON.Spring} */ null; + }, + + update: function(oldData) { + var el = this.el; + var data = this.data; + + if (!data.target) { + console.warn('Spring: invalid target specified.'); + return; + } + + // wait until the CANNON bodies is created and attached + if (!el.body || !data.target.body) { + (el.body ? data.target : el).addEventListener('body-loaded', this.update.bind(this, {})); + return; + } + + // create the spring if necessary + this.createSpring(); + // apply new data to the spring + this.updateSpring(oldData); + }, + + updateSpring: function(oldData) { + if (!this.spring) { + console.warn('Spring: Component attempted to change spring before its created. No changes made.'); + return; + } + var data = this.data; + var spring = this.spring; + + // Cycle through the schema and check if an attribute has changed. + // if so, apply it to the spring + Object.keys(data).forEach(function(attr) { + if (data[attr] !== oldData[attr]) { + if (attr === 'target') { + // special case for the target selector + spring.bodyB = data.target.body; + return; + } + spring[attr] = data[attr]; + } + }) + }, + + createSpring: function() { + if (this.spring) return; // no need to create a new spring + this.spring = new CANNON.Spring(this.el.body); + }, + + // If the spring is valid, update the force each tick the physics are calculated + step: function(t, dt) { + return this.spring && this.isActive ? this.spring.applyForce() : void 0; + }, + + // resume updating the force when component upon calling play() + play: function() { + this.isActive = true; + }, + + // stop updating the force when component upon calling stop() + pause: function() { + this.isActive = false; + }, + + //remove the event listener + delete the spring + remove: function() { + if (this.spring) + delete this.spring; + this.spring = null; + } +}) + +},{"cannon-es":5}],20:[function(require,module,exports){ +module.exports = { + GRAVITY: -9.8, + MAX_INTERVAL: 4 / 60, + ITERATIONS: 10, + CONTACT_MATERIAL: { + friction: 0.01, + restitution: 0.3, + contactEquationStiffness: 1e8, + contactEquationRelaxation: 3, + frictionEquationStiffness: 1e8, + frictionEquationRegularization: 3 + }, + ACTIVATION_STATE: { + ACTIVE_TAG: "active", + ISLAND_SLEEPING: "islandSleeping", + WANTS_DEACTIVATION: "wantsDeactivation", + DISABLE_DEACTIVATION: "disableDeactivation", + DISABLE_SIMULATION: "disableSimulation" + }, + COLLISION_FLAG: { + STATIC_OBJECT: 1, + KINEMATIC_OBJECT: 2, + NO_CONTACT_RESPONSE: 4, + CUSTOM_MATERIAL_CALLBACK: 8, //this allows per-triangle material (friction/restitution) + CHARACTER_OBJECT: 16, + DISABLE_VISUALIZE_OBJECT: 32, //disable debug drawing + DISABLE_SPU_COLLISION_PROCESSING: 64 //disable parallel/SPU processing + }, + TYPE: { + STATIC: "static", + DYNAMIC: "dynamic", + KINEMATIC: "kinematic" + }, + SHAPE: { + BOX: "box", + CYLINDER: "cylinder", + SPHERE: "sphere", + CAPSULE: "capsule", + CONE: "cone", + HULL: "hull", + HACD: "hacd", + VHACD: "vhacd", + MESH: "mesh", + HEIGHTFIELD: "heightfield" + }, + FIT: { + ALL: "all", + MANUAL: "manual" + }, + CONSTRAINT: { + LOCK: "lock", + FIXED: "fixed", + SPRING: "spring", + SLIDER: "slider", + HINGE: "hinge", + CONE_TWIST: "coneTwist", + POINT_TO_POINT: "pointToPoint" + } +}; + +},{}],21:[function(require,module,exports){ +/* global THREE */ +const Driver = require("./driver"); + +if (typeof window !== 'undefined') { + window.AmmoModule = window.Ammo; + window.Ammo = null; +} + +const EPS = 10e-6; + +function AmmoDriver() { + this.collisionConfiguration = null; + this.dispatcher = null; + this.broadphase = null; + this.solver = null; + this.physicsWorld = null; + this.debugDrawer = null; + + this.els = new Map(); + this.eventListeners = []; + this.collisions = new Map(); + this.collisionKeys = []; + this.currentCollisions = new Map(); +} + +AmmoDriver.prototype = new Driver(); +AmmoDriver.prototype.constructor = AmmoDriver; + +module.exports = AmmoDriver; + +/* @param {object} worldConfig */ +AmmoDriver.prototype.init = function(worldConfig) { + //Emscripten doesn't use real promises, just a .then() callback, so it necessary to wrap in a real promise. + return new Promise(resolve => { + AmmoModule().then(result => { + Ammo = result; + this.epsilon = worldConfig.epsilon || EPS; + this.debugDrawMode = worldConfig.debugDrawMode || THREE.AmmoDebugConstants.NoDebug; + this.maxSubSteps = worldConfig.maxSubSteps || 4; + this.fixedTimeStep = worldConfig.fixedTimeStep || 1 / 60; + this.collisionConfiguration = new Ammo.btDefaultCollisionConfiguration(); + this.dispatcher = new Ammo.btCollisionDispatcher(this.collisionConfiguration); + this.broadphase = new Ammo.btDbvtBroadphase(); + this.solver = new Ammo.btSequentialImpulseConstraintSolver(); + this.physicsWorld = new Ammo.btDiscreteDynamicsWorld( + this.dispatcher, + this.broadphase, + this.solver, + this.collisionConfiguration + ); + this.physicsWorld.setForceUpdateAllAabbs(false); + this.physicsWorld.setGravity( + new Ammo.btVector3(0, worldConfig.hasOwnProperty("gravity") ? worldConfig.gravity : -9.8, 0) + ); + this.physicsWorld.getSolverInfo().set_m_numIterations(worldConfig.solverIterations); + resolve(); + }); + }); +}; + +/* @param {Ammo.btCollisionObject} body */ +AmmoDriver.prototype.addBody = function(body, group, mask) { + this.physicsWorld.addRigidBody(body, group, mask); + const bodyptr = Ammo.getPointer(body); + this.els.set(bodyptr, body.el); + this.collisions.set(bodyptr, []); + this.collisionKeys.push(bodyptr); + this.currentCollisions.set(bodyptr, new Set()); +}; + +/* @param {Ammo.btCollisionObject} body */ +AmmoDriver.prototype.removeBody = function(body) { + this.physicsWorld.removeRigidBody(body); + this.removeEventListener(body); + const bodyptr = Ammo.getPointer(body); + this.els.delete(bodyptr); + this.collisions.delete(bodyptr); + this.collisionKeys.splice(this.collisionKeys.indexOf(bodyptr), 1); + this.currentCollisions.delete(bodyptr); +}; + +AmmoDriver.prototype.updateBody = function(body) { + if (this.els.has(Ammo.getPointer(body))) { + this.physicsWorld.updateSingleAabb(body); + } +}; + +/* @param {number} deltaTime */ +AmmoDriver.prototype.step = function(deltaTime) { + this.physicsWorld.stepSimulation(deltaTime, this.maxSubSteps, this.fixedTimeStep); + + const numManifolds = this.dispatcher.getNumManifolds(); + for (let i = 0; i < numManifolds; i++) { + const persistentManifold = this.dispatcher.getManifoldByIndexInternal(i); + const numContacts = persistentManifold.getNumContacts(); + const body0ptr = Ammo.getPointer(persistentManifold.getBody0()); + const body1ptr = Ammo.getPointer(persistentManifold.getBody1()); + let collided = false; + + for (let j = 0; j < numContacts; j++) { + const manifoldPoint = persistentManifold.getContactPoint(j); + const distance = manifoldPoint.getDistance(); + if (distance <= this.epsilon) { + collided = true; + break; + } + } + + if (collided) { + if (this.collisions.get(body0ptr).indexOf(body1ptr) === -1) { + this.collisions.get(body0ptr).push(body1ptr); + if (this.eventListeners.indexOf(body0ptr) !== -1) { + this.els.get(body0ptr).emit("collidestart", { targetEl: this.els.get(body1ptr) }); + } + if (this.eventListeners.indexOf(body1ptr) !== -1) { + this.els.get(body1ptr).emit("collidestart", { targetEl: this.els.get(body0ptr) }); + } + } + this.currentCollisions.get(body0ptr).add(body1ptr); + } + } + + for (let i = 0; i < this.collisionKeys.length; i++) { + const body0ptr = this.collisionKeys[i]; + const body1ptrs = this.collisions.get(body0ptr); + for (let j = body1ptrs.length - 1; j >= 0; j--) { + const body1ptr = body1ptrs[j]; + if (this.currentCollisions.get(body0ptr).has(body1ptr)) { + continue; + } + if (this.eventListeners.indexOf(body0ptr) !== -1) { + this.els.get(body0ptr).emit("collideend", { targetEl: this.els.get(body1ptr) }); + } + if (this.eventListeners.indexOf(body1ptr) !== -1) { + this.els.get(body1ptr).emit("collideend", { targetEl: this.els.get(body0ptr) }); + } + body1ptrs.splice(j, 1); + } + this.currentCollisions.get(body0ptr).clear(); + } + + if (this.debugDrawer) { + this.debugDrawer.update(); + } +}; + +/* @param {?} constraint */ +AmmoDriver.prototype.addConstraint = function(constraint) { + this.physicsWorld.addConstraint(constraint, false); +}; + +/* @param {?} constraint */ +AmmoDriver.prototype.removeConstraint = function(constraint) { + this.physicsWorld.removeConstraint(constraint); +}; + +/* @param {Ammo.btCollisionObject} body */ +AmmoDriver.prototype.addEventListener = function(body) { + this.eventListeners.push(Ammo.getPointer(body)); +}; + +/* @param {Ammo.btCollisionObject} body */ +AmmoDriver.prototype.removeEventListener = function(body) { + const ptr = Ammo.getPointer(body); + if (this.eventListeners.indexOf(ptr) !== -1) { + this.eventListeners.splice(this.eventListeners.indexOf(ptr), 1); + } +}; + +AmmoDriver.prototype.destroy = function() { + Ammo.destroy(this.collisionConfiguration); + Ammo.destroy(this.dispatcher); + Ammo.destroy(this.broadphase); + Ammo.destroy(this.solver); + Ammo.destroy(this.physicsWorld); + Ammo.destroy(this.debugDrawer); +}; + +/** + * @param {THREE.Scene} scene + * @param {object} options + */ +AmmoDriver.prototype.getDebugDrawer = function(scene, options) { + if (!this.debugDrawer) { + options = options || {}; + options.debugDrawMode = options.debugDrawMode || this.debugDrawMode; + this.debugDrawer = new THREE.AmmoDebugDrawer(scene, this.physicsWorld, options); + } + return this.debugDrawer; +}; + +},{"./driver":22}],22:[function(require,module,exports){ +/** + * Driver - defines limited API to local and remote physics controllers. + */ + +function Driver () {} + +module.exports = Driver; + +/****************************************************************************** + * Lifecycle + */ + +/* @param {object} worldConfig */ +Driver.prototype.init = abstractMethod; + +/* @param {number} deltaMS */ +Driver.prototype.step = abstractMethod; + +Driver.prototype.destroy = abstractMethod; + +/****************************************************************************** + * Bodies + */ + +/* @param {CANNON.Body} body */ +Driver.prototype.addBody = abstractMethod; + +/* @param {CANNON.Body} body */ +Driver.prototype.removeBody = abstractMethod; + +/** + * @param {CANNON.Body} body + * @param {string} methodName + * @param {Array} args + */ +Driver.prototype.applyBodyMethod = abstractMethod; + +/** @param {CANNON.Body} body */ +Driver.prototype.updateBodyProperties = abstractMethod; + +/****************************************************************************** + * Materials + */ + +/** @param {object} materialConfig */ +Driver.prototype.addMaterial = abstractMethod; + +/** + * @param {string} materialName1 + * @param {string} materialName2 + * @param {object} contactMaterialConfig + */ +Driver.prototype.addContactMaterial = abstractMethod; + +/****************************************************************************** + * Constraints + */ + +/* @param {CANNON.Constraint} constraint */ +Driver.prototype.addConstraint = abstractMethod; + +/* @param {CANNON.Constraint} constraint */ +Driver.prototype.removeConstraint = abstractMethod; + +/****************************************************************************** + * Contacts + */ + +/** @return {Array} */ +Driver.prototype.getContacts = abstractMethod; + +/*****************************************************************************/ + +function abstractMethod () { + throw new Error('Method not implemented.'); +} + +},{}],23:[function(require,module,exports){ +module.exports = { + INIT: 'init', + STEP: 'step', + + // Bodies. + ADD_BODY: 'add-body', + REMOVE_BODY: 'remove-body', + APPLY_BODY_METHOD: 'apply-body-method', + UPDATE_BODY_PROPERTIES: 'update-body-properties', + + // Materials. + ADD_MATERIAL: 'add-material', + ADD_CONTACT_MATERIAL: 'add-contact-material', + + // Constraints. + ADD_CONSTRAINT: 'add-constraint', + REMOVE_CONSTRAINT: 'remove-constraint', + + // Events. + COLLIDE: 'collide' +}; + +},{}],24:[function(require,module,exports){ +var CANNON = require('cannon-es'), + Driver = require('./driver'); + +function LocalDriver () { + this.world = null; + this.materials = {}; + this.contactMaterial = null; +} + +LocalDriver.prototype = new Driver(); +LocalDriver.prototype.constructor = LocalDriver; + +module.exports = LocalDriver; + +/****************************************************************************** + * Lifecycle + */ + +/* @param {object} worldConfig */ +LocalDriver.prototype.init = function (worldConfig) { + var world = new CANNON.World(); + world.quatNormalizeSkip = worldConfig.quatNormalizeSkip; + world.quatNormalizeFast = worldConfig.quatNormalizeFast; + world.solver.iterations = worldConfig.solverIterations; + world.gravity.set(0, worldConfig.gravity, 0); + world.broadphase = new CANNON.NaiveBroadphase(); + + this.world = world; +}; + +/* @param {number} deltaMS */ +LocalDriver.prototype.step = function (deltaMS) { + this.world.step(deltaMS); +}; + +LocalDriver.prototype.destroy = function () { + delete this.world; + delete this.contactMaterial; + this.materials = {}; +}; + +/****************************************************************************** + * Bodies + */ + +/* @param {CANNON.Body} body */ +LocalDriver.prototype.addBody = function (body) { + this.world.addBody(body); +}; + +/* @param {CANNON.Body} body */ +LocalDriver.prototype.removeBody = function (body) { + this.world.removeBody(body); +}; + +/** + * @param {CANNON.Body} body + * @param {string} methodName + * @param {Array} args + */ +LocalDriver.prototype.applyBodyMethod = function (body, methodName, args) { + body['__' + methodName].apply(body, args); +}; + +/** @param {CANNON.Body} body */ +LocalDriver.prototype.updateBodyProperties = function () {}; + +/****************************************************************************** + * Materials + */ + +/** + * @param {string} name + * @return {CANNON.Material} + */ +LocalDriver.prototype.getMaterial = function (name) { + return this.materials[name]; +}; + +/** @param {object} materialConfig */ +LocalDriver.prototype.addMaterial = function (materialConfig) { + this.materials[materialConfig.name] = new CANNON.Material(materialConfig); + this.materials[materialConfig.name].name = materialConfig.name; +}; + +/** + * @param {string} matName1 + * @param {string} matName2 + * @param {object} contactMaterialConfig + */ +LocalDriver.prototype.addContactMaterial = function (matName1, matName2, contactMaterialConfig) { + var mat1 = this.materials[matName1], + mat2 = this.materials[matName2]; + this.contactMaterial = new CANNON.ContactMaterial(mat1, mat2, contactMaterialConfig); + this.world.addContactMaterial(this.contactMaterial); +}; + +/****************************************************************************** + * Constraints + */ + +/* @param {CANNON.Constraint} constraint */ +LocalDriver.prototype.addConstraint = function (constraint) { + if (!constraint.type) { + if (constraint instanceof CANNON.LockConstraint) { + constraint.type = 'LockConstraint'; + } else if (constraint instanceof CANNON.DistanceConstraint) { + constraint.type = 'DistanceConstraint'; + } else if (constraint instanceof CANNON.HingeConstraint) { + constraint.type = 'HingeConstraint'; + } else if (constraint instanceof CANNON.ConeTwistConstraint) { + constraint.type = 'ConeTwistConstraint'; + } else if (constraint instanceof CANNON.PointToPointConstraint) { + constraint.type = 'PointToPointConstraint'; + } + } + this.world.addConstraint(constraint); +}; + +/* @param {CANNON.Constraint} constraint */ +LocalDriver.prototype.removeConstraint = function (constraint) { + this.world.removeConstraint(constraint); +}; + +/****************************************************************************** + * Contacts + */ + +/** @return {Array} */ +LocalDriver.prototype.getContacts = function () { + return this.world.contacts; +}; + +},{"./driver":22,"cannon-es":5}],25:[function(require,module,exports){ +var Driver = require('./driver'); + +function NetworkDriver () { + throw new Error('[NetworkDriver] Driver not implemented.'); +} + +NetworkDriver.prototype = new Driver(); +NetworkDriver.prototype.constructor = NetworkDriver; + +module.exports = NetworkDriver; + +},{"./driver":22}],26:[function(require,module,exports){ +/** + * Stub version of webworkify, for debugging code outside of a webworker. + */ +function webworkifyDebug (worker) { + var targetA = new EventTarget(), + targetB = new EventTarget(); + + targetA.setTarget(targetB); + targetB.setTarget(targetA); + + worker(targetA); + return targetB; +} + +module.exports = webworkifyDebug; + +/****************************************************************************** + * EventTarget + */ + +function EventTarget () { + this.listeners = []; +} + +EventTarget.prototype.setTarget = function (target) { + this.target = target; +}; + +EventTarget.prototype.addEventListener = function (type, fn) { + this.listeners.push(fn); +}; + +EventTarget.prototype.dispatchEvent = function (type, event) { + for (var i = 0; i < this.listeners.length; i++) { + this.listeners[i](event); + } +}; + +EventTarget.prototype.postMessage = function (msg) { + this.target.dispatchEvent('message', {data: msg}); +}; + +},{}],27:[function(require,module,exports){ +/* global performance */ + +var webworkify = require('webworkify'), + webworkifyDebug = require('./webworkify-debug'), + Driver = require('./driver'), + Event = require('./event'), + worker = require('./worker'), + protocol = require('../utils/protocol'); + +var ID = protocol.ID; + +/****************************************************************************** + * Constructor + */ + +function WorkerDriver (options) { + this.fps = options.fps; + this.engine = options.engine; + this.interpolate = options.interpolate; + // Approximate number of physics steps to 'pad' rendering. + this.interpBufferSize = options.interpolationBufferSize; + this.debug = options.debug; + + this.bodies = {}; + this.contacts = []; + + // https://gafferongames.com/post/snapshot_interpolation/ + this.frameDelay = this.interpBufferSize * 1000 / this.fps; + this.frameBuffer = []; + + this.worker = this.debug + ? webworkifyDebug(worker) + : webworkify(worker); + this.worker.addEventListener('message', this._onMessage.bind(this)); +} + +WorkerDriver.prototype = new Driver(); +WorkerDriver.prototype.constructor = WorkerDriver; + +module.exports = WorkerDriver; + +/****************************************************************************** + * Lifecycle + */ + +/* @param {object} worldConfig */ +WorkerDriver.prototype.init = function (worldConfig) { + this.worker.postMessage({ + type: Event.INIT, + worldConfig: worldConfig, + fps: this.fps, + engine: this.engine + }); +}; + +/** + * Increments the physics world forward one step, if interpolation is enabled. + * If disabled, increments are performed as messages arrive. + * @param {number} deltaMS + */ +WorkerDriver.prototype.step = function () { + if (!this.interpolate) return; + + // Get the two oldest frames that haven't expired. Ideally we would use all + // available frames to keep things smooth, but lerping is easier and faster. + var prevFrame = this.frameBuffer[0]; + var nextFrame = this.frameBuffer[1]; + var timestamp = performance.now(); + while (prevFrame && nextFrame && timestamp - prevFrame.timestamp > this.frameDelay) { + this.frameBuffer.shift(); + prevFrame = this.frameBuffer[0]; + nextFrame = this.frameBuffer[1]; + } + + if (!prevFrame || !nextFrame) return; + + var mix = (timestamp - prevFrame.timestamp) / this.frameDelay; + mix = (mix - (1 - 1 / this.interpBufferSize)) * this.interpBufferSize; + + for (var id in prevFrame.bodies) { + if (prevFrame.bodies.hasOwnProperty(id) && nextFrame.bodies.hasOwnProperty(id)) { + protocol.deserializeInterpBodyUpdate( + prevFrame.bodies[id], + nextFrame.bodies[id], + this.bodies[id], + mix + ); + } + } +}; + +WorkerDriver.prototype.destroy = function () { + this.worker.terminate(); + delete this.worker; +}; + +/** {Event} event */ +WorkerDriver.prototype._onMessage = function (event) { + if (event.data.type === Event.STEP) { + var data = event.data, + bodies = data.bodies; + + this.contacts = event.data.contacts; + + // If interpolation is enabled, store the frame. If not, update all bodies + // immediately. + if (this.interpolate) { + this.frameBuffer.push({timestamp: performance.now(), bodies: bodies}); + } else { + for (var id in bodies) { + if (bodies.hasOwnProperty(id)) { + protocol.deserializeBodyUpdate(bodies[id], this.bodies[id]); + } + } + } + + } else if (event.data.type === Event.COLLIDE) { + var body = this.bodies[event.data.bodyID]; + var target = this.bodies[event.data.targetID]; + var contact = protocol.deserializeContact(event.data.contact, this.bodies); + if (!body._listeners || !body._listeners.collide) return; + for (var i = 0; i < body._listeners.collide.length; i++) { + body._listeners.collide[i]({target: target, body: body, contact: contact}); + } + + } else { + throw new Error('[WorkerDriver] Unexpected message type.'); + } +}; + +/****************************************************************************** + * Bodies + */ + +/* @param {CANNON.Body} body */ +WorkerDriver.prototype.addBody = function (body) { + protocol.assignID('body', body); + this.bodies[body[ID]] = body; + this.worker.postMessage({type: Event.ADD_BODY, body: protocol.serializeBody(body)}); +}; + +/* @param {CANNON.Body} body */ +WorkerDriver.prototype.removeBody = function (body) { + this.worker.postMessage({type: Event.REMOVE_BODY, bodyID: body[ID]}); + delete this.bodies[body[ID]]; +}; + +/** + * @param {CANNON.Body} body + * @param {string} methodName + * @param {Array} args + */ +WorkerDriver.prototype.applyBodyMethod = function (body, methodName, args) { + switch (methodName) { + case 'applyForce': + case 'applyImpulse': + this.worker.postMessage({ + type: Event.APPLY_BODY_METHOD, + bodyID: body[ID], + methodName: methodName, + args: [args[0].toArray(), args[1].toArray()] + }); + break; + default: + throw new Error('Unexpected methodName: %s', methodName); + } +}; -},{"../../constants":20,"ammo-debug-drawer":4,"three-to-ammo":6}],11:[function(require,module,exports){ -var CANNON = require('cannon-es'); -const { threeToCannon, ShapeType } = require('three-to-cannon'); -const identityQuaternion = new THREE.Quaternion() - -function mesh2shape (object, options) { - - const result = threeToCannon(object, options); - return result; -} - -require('../../../lib/CANNON-shape2mesh'); - -var Body = { - dependencies: ['velocity'], - - schema: { - mass: {default: 5, if: {type: 'dynamic'}}, - linearDamping: { default: 0.01, if: {type: 'dynamic'}}, - angularDamping: { default: 0.01, if: {type: 'dynamic'}}, - shape: {default: 'auto', oneOf: ['auto', 'box', 'cylinder', 'sphere', 'hull', 'mesh', 'none']}, - cylinderAxis: {default: 'y', oneOf: ['x', 'y', 'z']}, - sphereRadius: {default: NaN}, - type: {default: 'dynamic', oneOf: ['static', 'dynamic']} - }, - - /** - * Initializes a body component, assigning it to the physics system and binding listeners for - * parsing the elements geometry. - */ - init: function () { - this.system = this.el.sceneEl.systems.physics; - - if (this.el.sceneEl.hasLoaded) { - this.initBody(); - } else { - this.el.sceneEl.addEventListener('loaded', this.initBody.bind(this)); - } - }, - - /** - * Parses an element's geometry and component metadata to create a CANNON.Body instance for the - * component. - */ - initBody: function () { - var el = this.el, - data = this.data; - - var obj = this.el.object3D; - var pos = obj.position; - var quat = obj.quaternion; - - this.body = new CANNON.Body({ - mass: data.type === 'static' ? 0 : data.mass || 0, - material: this.system.getMaterial('defaultMaterial'), - position: new CANNON.Vec3(pos.x, pos.y, pos.z), - quaternion: new CANNON.Quaternion(quat.x, quat.y, quat.z, quat.w), - linearDamping: data.linearDamping, - angularDamping: data.angularDamping, - type: data.type === 'dynamic' ? CANNON.Body.DYNAMIC : CANNON.Body.STATIC, - }); - - // Matrix World must be updated at root level, if scale is to be applied – updateMatrixWorld() - // only checks an object's parent, not the rest of the ancestors. Hence, a wrapping entity with - // scale="0.5 0.5 0.5" will be ignored. - // Reference: https://github.com/mrdoob/three.js/blob/master/src/core/Object3D.js#L511-L541 - // Potential fix: https://github.com/mrdoob/three.js/pull/7019 - this.el.object3D.updateMatrixWorld(true); - - if(data.shape !== 'none') { - var options = data.shape === 'auto' ? undefined : AFRAME.utils.extend({}, this.data, { - type: ShapeType[data.shape.toUpperCase()] - }); - - const shapeInfo = mesh2shape(this.el.object3D, options); - let shape, offset, orientation; - if (shapeInfo) { - ({ shape, offset, orientation } = shapeInfo); - } - - if (!shape) { - el.addEventListener('object3dset', this.initBody.bind(this)); - return; - } - - this.body.addShape(shape, offset, orientation); - - // Show wireframe - if (this.system.debug) { - this.shouldUpdateWireframe = true; - } - - this.hasShape = true; - } - - this.el.body = this.body; - this.body.el = el; - - // If component wasn't initialized when play() was called, finish up. - if (this.isPlaying) { - this._play(); - } - - if (this.hasShape) { - this.el.emit('body-loaded', {body: this.el.body}); - } - }, - - addShape: function(shape, offset, orientation) { - if (this.data.shape !== 'none') { - console.warn('shape can only be added if shape property is none'); - return; - } - - if (!shape) { - console.warn('shape cannot be null'); - return; - } - - if (!this.body) { - console.warn('shape cannot be added before body is loaded'); - return; - } - this.body.addShape(shape, offset, orientation); - - if (this.system.debug) { - this.shouldUpdateWireframe = true; - } - - this.shouldUpdateBody = true; - }, - - tick: function () { - if (this.shouldUpdateBody) { - - // Calling play will result in the object being re-added to the - // physics system with the updated body / shape data. - // But we mustn't add it twice, so any previously loaded body should be paused first. - this._pause(); - this.hasShape = true; - this._play() - - this.el.emit('body-loaded', {body: this.el.body}); - this.shouldUpdateBody = false; - } - - if (this.shouldUpdateWireframe) { - this.createWireframe(this.body); - this.shouldUpdateWireframe = false; - } - }, - - /** - * Registers the component with the physics system, if ready. - */ - play: function () { - this._play(); - }, - - /** - * Internal helper to register component with physics system. - */ - _play: function () { - - if (!this.hasShape) return; - - this.syncToPhysics(); - this.system.addComponent(this); - this.system.addBody(this.body); - if (this.wireframe) this.el.sceneEl.object3D.add(this.wireframe); - }, - - /** - * Unregisters the component with the physics system. - */ - pause: function () { - this._pause(); - }, - - _pause: function () { - - if (!this.hasShape) return; - - this.system.removeComponent(this); - if (this.body) this.system.removeBody(this.body); - if (this.wireframe) this.el.sceneEl.object3D.remove(this.wireframe); - }, - - /** - * Updates the CANNON.Body instance, where possible. - */ - update: function (prevData) { - if (!this.body) return; - - var data = this.data; - - if (prevData.type != undefined && data.type != prevData.type) { - this.body.type = data.type === 'dynamic' ? CANNON.Body.DYNAMIC : CANNON.Body.STATIC; - } - - this.body.mass = data.mass || 0; - if (data.type === 'dynamic') { - this.body.linearDamping = data.linearDamping; - this.body.angularDamping = data.angularDamping; - } - if (data.mass !== prevData.mass) { - this.body.updateMassProperties(); - } - if (this.body.updateProperties) this.body.updateProperties(); - }, - - /** - * Removes the component and all physics and scene side effects. - */ - remove: function () { - if (this.body) { - delete this.body.el; - delete this.body; - } - delete this.el.body; - delete this.wireframe; - }, - - beforeStep: function () { - if (this.body.mass === 0) { - this.syncToPhysics(); - } - }, - - step: function () { - if (this.body.mass !== 0) { - this.syncFromPhysics(); - } - }, - - /** - * Creates a wireframe for the body, for debugging. - * TODO(donmccurdy) – Refactor this into a standalone utility or component. - * @param {CANNON.Body} body - * @param {CANNON.Shape} shape - */ - createWireframe: function (body) { - if (this.wireframe) { - this.el.sceneEl.object3D.remove(this.wireframe); - delete this.wireframe; - } - this.wireframe = new THREE.Object3D(); - this.el.sceneEl.object3D.add(this.wireframe); - - var offset, mesh; - var orientation = new THREE.Quaternion(); - for (var i = 0; i < this.body.shapes.length; i++) - { - offset = this.body.shapeOffsets[i], - orientation.copy(this.body.shapeOrientations[i]), - mesh = CANNON.shape2mesh(this.body).children[i]; - - var wireframe = new THREE.LineSegments( - new THREE.EdgesGeometry(mesh.geometry), - new THREE.LineBasicMaterial({color: 0xff0000}) - ); - - if (offset) { - wireframe.position.copy(offset); - } - - if (orientation) { - wireframe.quaternion.copy(orientation); - } - - this.wireframe.add(wireframe); - } - - this.syncWireframe(); - }, - - /** - * Updates the debugging wireframe's position and rotation. - */ - syncWireframe: function () { - var offset, - wireframe = this.wireframe; - - if (!this.wireframe) return; - - // Apply rotation. If the shape required custom orientation, also apply - // that on the wireframe. - wireframe.quaternion.copy(this.body.quaternion); - if (wireframe.orientation) { - wireframe.quaternion.multiply(wireframe.orientation); - } - - // Apply position. If the shape required custom offset, also apply that on - // the wireframe. - wireframe.position.copy(this.body.position); - if (wireframe.offset) { - offset = wireframe.offset.clone().applyQuaternion(wireframe.quaternion); - wireframe.position.add(offset); - } - - wireframe.updateMatrix(); - }, - - /** - * Updates the CANNON.Body instance's position, velocity, and rotation, based on the scene. - */ - syncToPhysics: (function () { - var q = new THREE.Quaternion(), - v = new THREE.Vector3(); - return function () { - var el = this.el, - parentEl = el.parentEl, - body = this.body; - - if (!body) return; - - if (el.components.velocity) body.velocity.copy(el.getAttribute('velocity')); - - if (parentEl.isScene) { - body.quaternion.copy(el.object3D.quaternion); - body.position.copy(el.object3D.position); - } else { - el.object3D.getWorldQuaternion(q); - body.quaternion.copy(q); - el.object3D.getWorldPosition(v); - body.position.copy(v); - } - - if (this.body.updateProperties) this.body.updateProperties(); - if (this.wireframe) this.syncWireframe(); - }; - }()), - - /** - * Updates the scene object's position and rotation, based on the physics simulation. - */ - syncFromPhysics: (function () { - var v = new THREE.Vector3(), - q1 = new THREE.Quaternion(), - q2 = new THREE.Quaternion(); - return function () { - var el = this.el, - parentEl = el.parentEl, - body = this.body; - - if (!body) return; - if (!parentEl) return; - - if (parentEl.isScene) { - el.object3D.quaternion.copy(body.quaternion); - el.object3D.position.copy(body.position); - } else { - q1.copy(body.quaternion); - parentEl.object3D.getWorldQuaternion(q2); - q1.premultiply(q2.invert()); - el.object3D.quaternion.copy(q1); - - v.copy(body.position); - parentEl.object3D.worldToLocal(v); - el.object3D.position.copy(v); - } - - if (this.wireframe) this.syncWireframe(); - }; - }()) -}; - -module.exports.definition = Body; -module.exports.Component = AFRAME.registerComponent('body', Body); +/** @param {CANNON.Body} body */ +WorkerDriver.prototype.updateBodyProperties = function (body) { + this.worker.postMessage({ + type: Event.UPDATE_BODY_PROPERTIES, + body: protocol.serializeBody(body) + }); +}; -},{"../../../lib/CANNON-shape2mesh":2,"cannon-es":5,"three-to-cannon":7}],12:[function(require,module,exports){ -var Body = require('./body'); - -/** - * Dynamic body. - * - * Moves according to physics simulation, and may collide with other objects. - */ -var DynamicBody = AFRAME.utils.extend({}, Body.definition); - -module.exports = AFRAME.registerComponent('dynamic-body', DynamicBody); +/****************************************************************************** + * Materials + */ -},{"./body":11}],13:[function(require,module,exports){ -var Body = require('./body'); - -/** - * Static body. - * - * Solid body with a fixed position. Unaffected by gravity and collisions, but - * other objects may collide with it. - */ -var StaticBody = AFRAME.utils.extend({}, Body.definition); - -StaticBody.schema = AFRAME.utils.extend({}, Body.definition.schema, { - type: {default: 'static', oneOf: ['static', 'dynamic']}, - mass: {default: 0} -}); - -module.exports = AFRAME.registerComponent('static-body', StaticBody); +/** + * @param {string} name + * @return {CANNON.Material} + */ +WorkerDriver.prototype.getMaterial = function (name) { + // No access to materials here. Eventually we might return the name or ID, if + // multiple materials were selected, but for now there's only one and it's safe + // to assume the worker is already using it. + return undefined; +}; -},{"./body":11}],14:[function(require,module,exports){ -var CANNON = require("cannon-es"); - -module.exports = AFRAME.registerComponent("constraint", { - multiple: true, - - schema: { - // Type of constraint. - type: { default: "lock", oneOf: ["coneTwist", "distance", "hinge", "lock", "pointToPoint"] }, - - // Target (other) body for the constraint. - target: { type: "selector" }, - - // Maximum force that should be applied to constraint the bodies. - maxForce: { default: 1e6, min: 0 }, - - // If true, bodies can collide when they are connected. - collideConnected: { default: true }, - - // Wake up bodies when connected. - wakeUpBodies: { default: true }, - - // The distance to be kept between the bodies. If 0, will be set to current distance. - distance: { default: 0, min: 0 }, - - // Offset of the hinge or point-to-point constraint, defined locally in the body. - pivot: { type: "vec3" }, - targetPivot: { type: "vec3" }, - - // An axis that each body can rotate around, defined locally to that body. - axis: { type: "vec3", default: { x: 0, y: 0, z: 1 } }, - targetAxis: { type: "vec3", default: { x: 0, y: 0, z: 1 } } - }, - - init: function() { - this.system = this.el.sceneEl.systems.physics; - this.constraint = /* {CANNON.Constraint} */ null; - }, - - remove: function() { - if (!this.constraint) return; - - this.system.removeConstraint(this.constraint); - this.constraint = null; - }, - - update: function() { - var el = this.el, - data = this.data; - - this.remove(); - - if (!el.body || !data.target.body) { - (el.body ? data.target : el).addEventListener("body-loaded", this.update.bind(this, {})); - return; - } - - this.constraint = this.createConstraint(); - this.system.addConstraint(this.constraint); - }, - - /** - * Creates a new constraint, given current component data. The CANNON.js constructors are a bit - * different for each constraint type. A `.type` property is added to each constraint, because - * `instanceof` checks are not reliable for some types. These types are needed for serialization. - * @return {CANNON.Constraint} - */ - createConstraint: function() { - var constraint, - data = this.data, - pivot = new CANNON.Vec3(data.pivot.x, data.pivot.y, data.pivot.z), - targetPivot = new CANNON.Vec3(data.targetPivot.x, data.targetPivot.y, data.targetPivot.z), - axis = new CANNON.Vec3(data.axis.x, data.axis.y, data.axis.z), - targetAxis = new CANNON.Vec3(data.targetAxis.x, data.targetAxis.y, data.targetAxis.z); - - var constraint; - - switch (data.type) { - case "lock": - constraint = new CANNON.LockConstraint(this.el.body, data.target.body, { maxForce: data.maxForce }); - constraint.type = "LockConstraint"; - break; - - case "distance": - constraint = new CANNON.DistanceConstraint(this.el.body, data.target.body, data.distance, data.maxForce); - constraint.type = "DistanceConstraint"; - break; - - case "hinge": - constraint = new CANNON.HingeConstraint(this.el.body, data.target.body, { - pivotA: pivot, - pivotB: targetPivot, - axisA: axis, - axisB: targetAxis, - maxForce: data.maxForce - }); - constraint.type = "HingeConstraint"; - break; - - case "coneTwist": - constraint = new CANNON.ConeTwistConstraint(this.el.body, data.target.body, { - pivotA: pivot, - pivotB: targetPivot, - axisA: axis, - axisB: targetAxis, - maxForce: data.maxForce - }); - constraint.type = "ConeTwistConstraint"; - break; - - case "pointToPoint": - constraint = new CANNON.PointToPointConstraint( - this.el.body, - pivot, - data.target.body, - targetPivot, - data.maxForce - ); - constraint.type = "PointToPointConstraint"; - break; - - default: - throw new Error("[constraint] Unexpected type: " + data.type); - } - - constraint.collideConnected = data.collideConnected; - return constraint; - } -}); +/** @param {object} materialConfig */ +WorkerDriver.prototype.addMaterial = function (materialConfig) { + this.worker.postMessage({type: Event.ADD_MATERIAL, materialConfig: materialConfig}); +}; -},{"cannon-es":5}],15:[function(require,module,exports){ -module.exports = { - 'velocity': require('./velocity'), - - registerAll: function (AFRAME) { - if (this._registered) return; - - AFRAME = AFRAME || window.AFRAME; - - if (!AFRAME.components['velocity']) AFRAME.registerComponent('velocity', this.velocity); - - this._registered = true; - } -}; +/** + * @param {string} matName1 + * @param {string} matName2 + * @param {object} contactMaterialConfig + */ +WorkerDriver.prototype.addContactMaterial = function (matName1, matName2, contactMaterialConfig) { + this.worker.postMessage({ + type: Event.ADD_CONTACT_MATERIAL, + materialName1: matName1, + materialName2: matName2, + contactMaterialConfig: contactMaterialConfig + }); +}; + +/****************************************************************************** + * Constraints + */ + +/* @param {CANNON.Constraint} constraint */ +WorkerDriver.prototype.addConstraint = function (constraint) { + if (!constraint.type) { + if (constraint instanceof CANNON.LockConstraint) { + constraint.type = 'LockConstraint'; + } else if (constraint instanceof CANNON.DistanceConstraint) { + constraint.type = 'DistanceConstraint'; + } else if (constraint instanceof CANNON.HingeConstraint) { + constraint.type = 'HingeConstraint'; + } else if (constraint instanceof CANNON.ConeTwistConstraint) { + constraint.type = 'ConeTwistConstraint'; + } else if (constraint instanceof CANNON.PointToPointConstraint) { + constraint.type = 'PointToPointConstraint'; + } + } + protocol.assignID('constraint', constraint); + this.worker.postMessage({ + type: Event.ADD_CONSTRAINT, + constraint: protocol.serializeConstraint(constraint) + }); +}; + +/* @param {CANNON.Constraint} constraint */ +WorkerDriver.prototype.removeConstraint = function (constraint) { + this.worker.postMessage({ + type: Event.REMOVE_CONSTRAINT, + constraintID: constraint[ID] + }); +}; + +/****************************************************************************** + * Contacts + */ + +/** @return {Array} */ +WorkerDriver.prototype.getContacts = function () { + // TODO(donmccurdy): There's some wasted memory allocation here. + var bodies = this.bodies; + return this.contacts.map(function (message) { + return protocol.deserializeContact(message, bodies); + }); +}; + +},{"../utils/protocol":31,"./driver":22,"./event":23,"./webworkify-debug":26,"./worker":28,"webworkify":8}],28:[function(require,module,exports){ +var Event = require('./event'), + LocalDriver = require('./local-driver'), + AmmoDriver = require('./ammo-driver'), + protocol = require('../utils/protocol'); + +var ID = protocol.ID; + +module.exports = function (self) { + var driver = null; + var bodies = {}; + var constraints = {}; + var stepSize; + + self.addEventListener('message', function (event) { + var data = event.data; + + switch (data.type) { + // Lifecycle. + case Event.INIT: + driver = data.engine === 'cannon' + ? new LocalDriver() + : new AmmoDriver(); + driver.init(data.worldConfig); + stepSize = 1 / data.fps; + setInterval(step, 1000 / data.fps); + break; + + // Bodies. + case Event.ADD_BODY: + var body = protocol.deserializeBody(data.body); + body.material = driver.getMaterial( 'defaultMaterial' ); + bodies[body[ID]] = body; + + body.addEventListener('collide', function (evt) { + var message = { + type: Event.COLLIDE, + bodyID: evt.target[ID], // set the target as the body to be identical to the local driver + targetID: evt.body[ID], // set the body as the target to be identical to the local driver + contact: protocol.serializeContact(evt.contact) + } + self.postMessage(message); + }); + driver.addBody(body); + break; + case Event.REMOVE_BODY: + driver.removeBody(bodies[data.bodyID]); + delete bodies[data.bodyID]; + break; + case Event.APPLY_BODY_METHOD: + bodies[data.bodyID][data.methodName]( + protocol.deserializeVec3(data.args[0]), + protocol.deserializeVec3(data.args[1]) + ); + break; + case Event.UPDATE_BODY_PROPERTIES: + protocol.deserializeBodyUpdate(data.body, bodies[data.body.id]); + break; + + // Materials. + case Event.ADD_MATERIAL: + driver.addMaterial(data.materialConfig); + break; + case Event.ADD_CONTACT_MATERIAL: + driver.addContactMaterial( + data.materialName1, + data.materialName2, + data.contactMaterialConfig + ); + break; + + // Constraints. + case Event.ADD_CONSTRAINT: + var constraint = protocol.deserializeConstraint(data.constraint, bodies); + constraints[constraint[ID]] = constraint; + driver.addConstraint(constraint); + break; + case Event.REMOVE_CONSTRAINT: + driver.removeConstraint(constraints[data.constraintID]); + delete constraints[data.constraintID]; + break; + + default: + throw new Error('[Worker] Unexpected event type: %s', data.type); + + } + }); + + function step () { + driver.step(stepSize); + + var bodyMessages = {}; + Object.keys(bodies).forEach(function (id) { + bodyMessages[id] = protocol.serializeBody(bodies[id]); + }); + + self.postMessage({ + type: Event.STEP, + bodies: bodyMessages, + contacts: driver.getContacts().map(protocol.serializeContact) + }); + } +}; + +},{"../utils/protocol":31,"./ammo-driver":21,"./event":23,"./local-driver":24}],29:[function(require,module,exports){ +/* global THREE */ +var CANNON = require('cannon-es'), + CONSTANTS = require('./constants'), + C_GRAV = CONSTANTS.GRAVITY, + C_MAT = CONSTANTS.CONTACT_MATERIAL; + +const { TYPE } = require('./constants'); +var LocalDriver = require('./drivers/local-driver'), + WorkerDriver = require('./drivers/worker-driver'), + NetworkDriver = require('./drivers/network-driver'), + AmmoDriver = require('./drivers/ammo-driver'); +require('aframe-stats-panel') + +/** + * Physics system. + */ +module.exports = AFRAME.registerSystem('physics', { + schema: { + // CANNON.js driver type + driver: { default: 'local', oneOf: ['local', 'worker', 'network', 'ammo'] }, + networkUrl: { default: '', if: {driver: 'network'} }, + workerFps: { default: 60, if: {driver: 'worker'} }, + workerInterpolate: { default: true, if: {driver: 'worker'} }, + workerInterpBufferSize: { default: 2, if: {driver: 'worker'} }, + workerEngine: { default: 'cannon', if: {driver: 'worker'}, oneOf: ['cannon'] }, + workerDebug: { default: false, if: {driver: 'worker'} }, + + gravity: { default: C_GRAV }, + iterations: { default: CONSTANTS.ITERATIONS }, + friction: { default: C_MAT.friction }, + restitution: { default: C_MAT.restitution }, + contactEquationStiffness: { default: C_MAT.contactEquationStiffness }, + contactEquationRelaxation: { default: C_MAT.contactEquationRelaxation }, + frictionEquationStiffness: { default: C_MAT.frictionEquationStiffness }, + frictionEquationRegularization: { default: C_MAT.frictionEquationRegularization }, + + // Never step more than four frames at once. Effectively pauses the scene + // when out of focus, and prevents weird "jumps" when focus returns. + maxInterval: { default: 4 / 60 }, + + // If true, show wireframes around physics bodies. + debug: { default: false }, + + // If using ammo, set the default rendering mode for debug + debugDrawMode: { default: THREE.AmmoDebugConstants.NoDebug }, + // If using ammo, set the max number of steps per frame + maxSubSteps: { default: 4 }, + // If using ammo, set the framerate of the simulation + fixedTimeStep: { default: 1 / 60 }, + // Whether to output stats, and how to output them. One or more of "console", "events", "panel" + stats: {type: 'array', default: []} + }, + + /** + * Initializes the physics system. + */ + async init() { + var data = this.data; + + // If true, show wireframes around physics bodies. + this.debug = data.debug; + this.initStats(); + + this.callbacks = {beforeStep: [], step: [], afterStep: []}; + + this.listeners = {}; + + + this.driver = null; + switch (data.driver) { + case 'local': + this.driver = new LocalDriver(); + break; + + case 'ammo': + this.driver = new AmmoDriver(); + break; + + case 'network': + this.driver = new NetworkDriver(data.networkUrl); + break; -},{"./velocity":16}],16:[function(require,module,exports){ -/** - * Velocity, in m/s. - */ -module.exports = AFRAME.registerComponent('velocity', { - schema: {type: 'vec3'}, - - init: function () { - this.system = this.el.sceneEl.systems.physics; - - if (this.system) { - this.system.addComponent(this); - } - }, - - remove: function () { - if (this.system) { - this.system.removeComponent(this); - } - }, - - tick: function (t, dt) { - if (!dt) return; - if (this.system) return; - this.afterStep(t, dt); - }, - - afterStep: function (t, dt) { - if (!dt) return; - - var physics = this.el.sceneEl.systems.physics || {data: {maxInterval: 1 / 60}}, - - // TODO - There's definitely a bug with getComputedAttribute and el.data. - velocity = this.el.getAttribute('velocity') || {x: 0, y: 0, z: 0}, - position = this.el.object3D.position || {x: 0, y: 0, z: 0}; - - dt = Math.min(dt, physics.data.maxInterval * 1000); - - this.el.object3D.position.set( - position.x + velocity.x * dt / 1000, - position.y + velocity.y * dt / 1000, - position.z + velocity.z * dt / 1000 - ); - } -}); + case 'worker': + this.driver = new WorkerDriver({ + fps: data.workerFps, + engine: data.workerEngine, + interpolate: data.workerInterpolate, + interpolationBufferSize: data.workerInterpBufferSize, + debug: data.workerDebug + }); + break; -},{}],17:[function(require,module,exports){ -/* global Ammo,THREE */ -const threeToAmmo = require("three-to-ammo"); -const CONSTANTS = require("../../constants"), - SHAPE = CONSTANTS.SHAPE, - FIT = CONSTANTS.FIT; - -var AmmoShape = { - schema: { - type: { - default: SHAPE.HULL, - oneOf: [ - SHAPE.BOX, - SHAPE.CYLINDER, - SHAPE.SPHERE, - SHAPE.CAPSULE, - SHAPE.CONE, - SHAPE.HULL, - SHAPE.HACD, - SHAPE.VHACD, - SHAPE.MESH, - SHAPE.HEIGHTFIELD - ] - }, - fit: { default: FIT.ALL, oneOf: [FIT.ALL, FIT.MANUAL] }, - halfExtents: { type: "vec3", default: { x: 1, y: 1, z: 1 } }, - minHalfExtent: { default: 0 }, - maxHalfExtent: { default: Number.POSITIVE_INFINITY }, - sphereRadius: { default: NaN }, - cylinderAxis: { default: "y", oneOf: ["x", "y", "z"] }, - margin: { default: 0.01 }, - offset: { type: "vec3", default: { x: 0, y: 0, z: 0 } }, - orientation: { type: "vec4", default: { x: 0, y: 0, z: 0, w: 1 } }, - heightfieldData: { default: [] }, - heightfieldDistance: { default: 1 }, - includeInvisible: { default: false } - }, - - multiple: true, - - init: function() { - if (this.data.fit !== FIT.MANUAL) { - if (this.el.object3DMap.mesh) { - this.mesh = this.el.object3DMap.mesh; - } else { - const self = this; - this.el.addEventListener("object3dset", function (e) { - if (e.detail.type === "mesh") { - self.init(); - } - }); - console.log("Cannot use FIT.ALL without object3DMap.mesh. Waiting for it to be set."); - return; - } - } - - this.system = this.el.sceneEl.systems.physics; - this.collisionShapes = []; - - let bodyEl = this.el; - this.body = bodyEl.components["ammo-body"] || null; - while (!this.body && bodyEl.parentNode != this.el.sceneEl) { - bodyEl = bodyEl.parentNode; - if (bodyEl.components["ammo-body"]) { - this.body = bodyEl.components["ammo-body"]; - } - } - if (!this.body) { - console.warn("body not found"); - return; - } - this.body.addShapeComponent(this); - }, - - getMesh: function() { - return this.mesh || null; - }, - - addShapes: function(collisionShapes) { - this.collisionShapes = collisionShapes; - }, - - getShapes: function() { - return this.collisionShapes; - }, - - remove: function() { - if (!this.body) { - return; - } - - this.body.removeShapeComponent(this); - - while (this.collisionShapes.length > 0) { - const collisionShape = this.collisionShapes.pop(); - collisionShape.destroy(); - Ammo.destroy(collisionShape.localTransform); - } - } -}; - -module.exports.definition = AmmoShape; -module.exports.Component = AFRAME.registerComponent("ammo-shape", AmmoShape); + default: + throw new Error('[physics] Driver not recognized: "%s".', data.driver); + } -},{"../../constants":20,"three-to-ammo":6}],18:[function(require,module,exports){ -var CANNON = require('cannon-es'); - -var Shape = { - schema: { - shape: {default: 'box', oneOf: ['box', 'sphere', 'cylinder']}, - offset: {type: 'vec3', default: {x: 0, y: 0, z: 0}}, - orientation: {type: 'vec4', default: {x: 0, y: 0, z: 0, w: 1}}, - - // sphere - radius: {type: 'number', default: 1, if: {shape: ['sphere']}}, - - // box - halfExtents: {type: 'vec3', default: {x: 0.5, y: 0.5, z: 0.5}, if: {shape: ['box']}}, - - // cylinder - radiusTop: {type: 'number', default: 1, if: {shape: ['cylinder']}}, - radiusBottom: {type: 'number', default: 1, if: {shape: ['cylinder']}}, - height: {type: 'number', default: 1, if: {shape: ['cylinder']}}, - numSegments: {type: 'int', default: 8, if: {shape: ['cylinder']}} - }, - - multiple: true, - - init: function() { - if (this.el.sceneEl.hasLoaded) { - this.initShape(); - } else { - this.el.sceneEl.addEventListener('loaded', this.initShape.bind(this)); - } - }, - - initShape: function() { - this.bodyEl = this.el; - var bodyType = this._findType(this.bodyEl); - var data = this.data; - - while (!bodyType && this.bodyEl.parentNode != this.el.sceneEl) { - this.bodyEl = this.bodyEl.parentNode; - bodyType = this._findType(this.bodyEl); - } - - if (!bodyType) { - console.warn('body not found'); - return; - } - - var scale = new THREE.Vector3(); - this.bodyEl.object3D.getWorldScale(scale); - var shape, offset, orientation; - - if (data.hasOwnProperty('offset')) { - offset = new CANNON.Vec3( - data.offset.x * scale.x, - data.offset.y * scale.y, - data.offset.z * scale.z - ); - } - - if (data.hasOwnProperty('orientation')) { - orientation = new CANNON.Quaternion(); - orientation.copy(data.orientation); - } - - switch(data.shape) { - case 'sphere': - shape = new CANNON.Sphere(data.radius * scale.x); - break; - case 'box': - var halfExtents = new CANNON.Vec3( - data.halfExtents.x * scale.x, - data.halfExtents.y * scale.y, - data.halfExtents.z * scale.z - ); - shape = new CANNON.Box(halfExtents); - break; - case 'cylinder': - shape = new CANNON.Cylinder( - data.radiusTop * scale.x, - data.radiusBottom * scale.x, - data.height * scale.y, - data.numSegments - ); - - //rotate by 90 degrees similar to mesh2shape:createCylinderShape - var quat = new CANNON.Quaternion(); - quat.setFromEuler(90 * THREE.MathUtils.DEG2RAD, 0, 0, 'XYZ').normalize(); - orientation.mult(quat, orientation); - break; - default: - console.warn(data.shape + ' shape not supported'); - return; - } - - if (this.bodyEl.body) { - this.bodyEl.components[bodyType].addShape(shape, offset, orientation); - } else { - this.bodyEl.addEventListener('body-loaded', function() { - this.bodyEl.components[bodyType].addShape(shape, offset, orientation); - }, {once: true}); - } - }, - - _findType: function(el) { - if (el.hasAttribute('body')) { - return 'body'; - } else if (el.hasAttribute('dynamic-body')) { - return 'dynamic-body'; - } else if (el.hasAttribute('static-body')) { - return'static-body'; - } - return null; - }, - - remove: function() { - if (this.bodyEl.parentNode) { - console.warn('removing shape component not currently supported'); - } - } -}; - -module.exports.definition = Shape; -module.exports.Component = AFRAME.registerComponent('shape', Shape); + if (data.driver !== 'ammo') { + await this.driver.init({ + quatNormalizeSkip: 0, + quatNormalizeFast: false, + solverIterations: data.iterations, + gravity: data.gravity, + }); + this.driver.addMaterial({name: 'defaultMaterial'}); + this.driver.addMaterial({name: 'staticMaterial'}); + this.driver.addContactMaterial('defaultMaterial', 'defaultMaterial', { + friction: data.friction, + restitution: data.restitution, + contactEquationStiffness: data.contactEquationStiffness, + contactEquationRelaxation: data.contactEquationRelaxation, + frictionEquationStiffness: data.frictionEquationStiffness, + frictionEquationRegularization: data.frictionEquationRegularization + }); + this.driver.addContactMaterial('staticMaterial', 'defaultMaterial', { + friction: 1.0, + restitution: 0.0, + contactEquationStiffness: data.contactEquationStiffness, + contactEquationRelaxation: data.contactEquationRelaxation, + frictionEquationStiffness: data.frictionEquationStiffness, + frictionEquationRegularization: data.frictionEquationRegularization + }); + } else { + await this.driver.init({ + gravity: data.gravity, + debugDrawMode: data.debugDrawMode, + solverIterations: data.iterations, + maxSubSteps: data.maxSubSteps, + fixedTimeStep: data.fixedTimeStep + }); + } -},{"cannon-es":5}],19:[function(require,module,exports){ -var CANNON = require('cannon-es'); - -module.exports = AFRAME.registerComponent('spring', { - - multiple: true, - - schema: { - // Target (other) body for the constraint. - target: {type: 'selector'}, - - // Length of the spring, when no force acts upon it. - restLength: {default: 1, min: 0}, - - // How much will the spring suppress the force. - stiffness: {default: 100, min: 0}, - - // Stretch factor of the spring. - damping: {default: 1, min: 0}, - - // Offsets. - localAnchorA: {type: 'vec3', default: {x: 0, y: 0, z: 0}}, - localAnchorB: {type: 'vec3', default: {x: 0, y: 0, z: 0}}, - }, - - init: function() { - this.system = this.el.sceneEl.systems.physics; - this.system.addComponent(this); - this.isActive = true; - this.spring = /* {CANNON.Spring} */ null; - }, - - update: function(oldData) { - var el = this.el; - var data = this.data; - - if (!data.target) { - console.warn('Spring: invalid target specified.'); - return; - } - - // wait until the CANNON bodies is created and attached - if (!el.body || !data.target.body) { - (el.body ? data.target : el).addEventListener('body-loaded', this.update.bind(this, {})); - return; - } - - // create the spring if necessary - this.createSpring(); - // apply new data to the spring - this.updateSpring(oldData); - }, - - updateSpring: function(oldData) { - if (!this.spring) { - console.warn('Spring: Component attempted to change spring before its created. No changes made.'); - return; - } - var data = this.data; - var spring = this.spring; - - // Cycle through the schema and check if an attribute has changed. - // if so, apply it to the spring - Object.keys(data).forEach(function(attr) { - if (data[attr] !== oldData[attr]) { - if (attr === 'target') { - // special case for the target selector - spring.bodyB = data.target.body; - return; - } - spring[attr] = data[attr]; - } - }) - }, - - createSpring: function() { - if (this.spring) return; // no need to create a new spring - this.spring = new CANNON.Spring(this.el.body); - }, - - // If the spring is valid, update the force each tick the physics are calculated - step: function(t, dt) { - return this.spring && this.isActive ? this.spring.applyForce() : void 0; - }, - - // resume updating the force when component upon calling play() - play: function() { - this.isActive = true; - }, - - // stop updating the force when component upon calling stop() - pause: function() { - this.isActive = false; - }, - - //remove the event listener + delete the spring - remove: function() { - if (this.spring) - delete this.spring; - this.spring = null; - } -}) + this.initialized = true; -},{"cannon-es":5}],20:[function(require,module,exports){ -module.exports = { - GRAVITY: -9.8, - MAX_INTERVAL: 4 / 60, - ITERATIONS: 10, - CONTACT_MATERIAL: { - friction: 0.01, - restitution: 0.3, - contactEquationStiffness: 1e8, - contactEquationRelaxation: 3, - frictionEquationStiffness: 1e8, - frictionEquationRegularization: 3 - }, - ACTIVATION_STATE: { - ACTIVE_TAG: "active", - ISLAND_SLEEPING: "islandSleeping", - WANTS_DEACTIVATION: "wantsDeactivation", - DISABLE_DEACTIVATION: "disableDeactivation", - DISABLE_SIMULATION: "disableSimulation" - }, - COLLISION_FLAG: { - STATIC_OBJECT: 1, - KINEMATIC_OBJECT: 2, - NO_CONTACT_RESPONSE: 4, - CUSTOM_MATERIAL_CALLBACK: 8, //this allows per-triangle material (friction/restitution) - CHARACTER_OBJECT: 16, - DISABLE_VISUALIZE_OBJECT: 32, //disable debug drawing - DISABLE_SPU_COLLISION_PROCESSING: 64 //disable parallel/SPU processing - }, - TYPE: { - STATIC: "static", - DYNAMIC: "dynamic", - KINEMATIC: "kinematic" - }, - SHAPE: { - BOX: "box", - CYLINDER: "cylinder", - SPHERE: "sphere", - CAPSULE: "capsule", - CONE: "cone", - HULL: "hull", - HACD: "hacd", - VHACD: "vhacd", - MESH: "mesh", - HEIGHTFIELD: "heightfield" - }, - FIT: { - ALL: "all", - MANUAL: "manual" - }, - CONSTRAINT: { - LOCK: "lock", - FIXED: "fixed", - SPRING: "spring", - SLIDER: "slider", - HINGE: "hinge", - CONE_TWIST: "coneTwist", - POINT_TO_POINT: "pointToPoint" - } -}; + if (this.debug) { + this.setDebug(true); + } + }, -},{}],21:[function(require,module,exports){ -/* global THREE */ -const Driver = require("./driver"); - -if (typeof window !== 'undefined') { - window.AmmoModule = window.Ammo; - window.Ammo = null; -} - -const EPS = 10e-6; - -function AmmoDriver() { - this.collisionConfiguration = null; - this.dispatcher = null; - this.broadphase = null; - this.solver = null; - this.physicsWorld = null; - this.debugDrawer = null; - - this.els = new Map(); - this.eventListeners = []; - this.collisions = new Map(); - this.collisionKeys = []; - this.currentCollisions = new Map(); -} - -AmmoDriver.prototype = new Driver(); -AmmoDriver.prototype.constructor = AmmoDriver; - -module.exports = AmmoDriver; - -/* @param {object} worldConfig */ -AmmoDriver.prototype.init = function(worldConfig) { - //Emscripten doesn't use real promises, just a .then() callback, so it necessary to wrap in a real promise. - return new Promise(resolve => { - AmmoModule().then(result => { - Ammo = result; - this.epsilon = worldConfig.epsilon || EPS; - this.debugDrawMode = worldConfig.debugDrawMode || THREE.AmmoDebugConstants.NoDebug; - this.maxSubSteps = worldConfig.maxSubSteps || 4; - this.fixedTimeStep = worldConfig.fixedTimeStep || 1 / 60; - this.collisionConfiguration = new Ammo.btDefaultCollisionConfiguration(); - this.dispatcher = new Ammo.btCollisionDispatcher(this.collisionConfiguration); - this.broadphase = new Ammo.btDbvtBroadphase(); - this.solver = new Ammo.btSequentialImpulseConstraintSolver(); - this.physicsWorld = new Ammo.btDiscreteDynamicsWorld( - this.dispatcher, - this.broadphase, - this.solver, - this.collisionConfiguration - ); - this.physicsWorld.setForceUpdateAllAabbs(false); - this.physicsWorld.setGravity( - new Ammo.btVector3(0, worldConfig.hasOwnProperty("gravity") ? worldConfig.gravity : -9.8, 0) - ); - this.physicsWorld.getSolverInfo().set_m_numIterations(worldConfig.solverIterations); - resolve(); - }); - }); -}; - -/* @param {Ammo.btCollisionObject} body */ -AmmoDriver.prototype.addBody = function(body, group, mask) { - this.physicsWorld.addRigidBody(body, group, mask); - this.els.set(Ammo.getPointer(body), body.el); -}; - -/* @param {Ammo.btCollisionObject} body */ -AmmoDriver.prototype.removeBody = function(body) { - this.physicsWorld.removeRigidBody(body); - this.removeEventListener(body); - const bodyptr = Ammo.getPointer(body); - this.els.delete(bodyptr); - this.collisions.delete(bodyptr); - this.collisionKeys.splice(this.collisionKeys.indexOf(bodyptr), 1); - this.currentCollisions.delete(bodyptr); -}; - -AmmoDriver.prototype.updateBody = function(body) { - if (this.els.has(Ammo.getPointer(body))) { - this.physicsWorld.updateSingleAabb(body); - } -}; - -/* @param {number} deltaTime */ -AmmoDriver.prototype.step = function(deltaTime) { - this.physicsWorld.stepSimulation(deltaTime, this.maxSubSteps, this.fixedTimeStep); - - const numManifolds = this.dispatcher.getNumManifolds(); - for (let i = 0; i < numManifolds; i++) { - const persistentManifold = this.dispatcher.getManifoldByIndexInternal(i); - const numContacts = persistentManifold.getNumContacts(); - const body0ptr = Ammo.getPointer(persistentManifold.getBody0()); - const body1ptr = Ammo.getPointer(persistentManifold.getBody1()); - let collided = false; - - for (let j = 0; j < numContacts; j++) { - const manifoldPoint = persistentManifold.getContactPoint(j); - const distance = manifoldPoint.getDistance(); - if (distance <= this.epsilon) { - collided = true; - break; - } - } - - if (collided) { - if (!this.collisions.has(body0ptr)) { - this.collisions.set(body0ptr, []); - this.collisionKeys.push(body0ptr); - } - if (this.collisions.get(body0ptr).indexOf(body1ptr) === -1) { - this.collisions.get(body0ptr).push(body1ptr); - if (this.eventListeners.indexOf(body0ptr) !== -1) { - this.els.get(body0ptr).emit("collidestart", { targetEl: this.els.get(body1ptr) }); - } - if (this.eventListeners.indexOf(body1ptr) !== -1) { - this.els.get(body1ptr).emit("collidestart", { targetEl: this.els.get(body0ptr) }); - } - } - if (!this.currentCollisions.has(body0ptr)) { - this.currentCollisions.set(body0ptr, new Set()); - } - this.currentCollisions.get(body0ptr).add(body1ptr); - } - } - - for (let i = 0; i < this.collisionKeys.length; i++) { - const body0ptr = this.collisionKeys[i]; - const body1ptrs = this.collisions.get(body0ptr); - for (let j = body1ptrs.length - 1; j >= 0; j--) { - const body1ptr = body1ptrs[j]; - if (this.currentCollisions.get(body0ptr).has(body1ptr)) { - continue; - } - if (this.eventListeners.indexOf(body0ptr) !== -1) { - this.els.get(body0ptr).emit("collideend", { targetEl: this.els.get(body1ptr) }); - } - if (this.eventListeners.indexOf(body1ptr) !== -1) { - this.els.get(body1ptr).emit("collideend", { targetEl: this.els.get(body0ptr) }); - } - body1ptrs.splice(j, 1); - } - this.currentCollisions.get(body0ptr).clear(); - } - - if (this.debugDrawer) { - this.debugDrawer.update(); - } -}; - -/* @param {?} constraint */ -AmmoDriver.prototype.addConstraint = function(constraint) { - this.physicsWorld.addConstraint(constraint, false); -}; - -/* @param {?} constraint */ -AmmoDriver.prototype.removeConstraint = function(constraint) { - this.physicsWorld.removeConstraint(constraint); -}; - -/* @param {Ammo.btCollisionObject} body */ -AmmoDriver.prototype.addEventListener = function(body) { - this.eventListeners.push(Ammo.getPointer(body)); -}; - -/* @param {Ammo.btCollisionObject} body */ -AmmoDriver.prototype.removeEventListener = function(body) { - const ptr = Ammo.getPointer(body); - if (this.eventListeners.indexOf(ptr) !== -1) { - this.eventListeners.splice(this.eventListeners.indexOf(ptr), 1); - } -}; - -AmmoDriver.prototype.destroy = function() { - Ammo.destroy(this.collisionConfiguration); - Ammo.destroy(this.dispatcher); - Ammo.destroy(this.broadphase); - Ammo.destroy(this.solver); - Ammo.destroy(this.physicsWorld); - Ammo.destroy(this.debugDrawer); -}; - -/** - * @param {THREE.Scene} scene - * @param {object} options - */ -AmmoDriver.prototype.getDebugDrawer = function(scene, options) { - if (!this.debugDrawer) { - options = options || {}; - options.debugDrawMode = options.debugDrawMode || this.debugDrawMode; - this.debugDrawer = new THREE.AmmoDebugDrawer(scene, this.physicsWorld, options); - } - return this.debugDrawer; -}; + initStats() { + // Data used for performance monitoring. + this.statsToConsole = this.data.stats.includes("console") + this.statsToEvents = this.data.stats.includes("events") + this.statsToPanel = this.data.stats.includes("panel") -},{"./driver":22}],22:[function(require,module,exports){ -/** - * Driver - defines limited API to local and remote physics controllers. - */ - -function Driver () {} - -module.exports = Driver; - -/****************************************************************************** - * Lifecycle - */ - -/* @param {object} worldConfig */ -Driver.prototype.init = abstractMethod; - -/* @param {number} deltaMS */ -Driver.prototype.step = abstractMethod; - -Driver.prototype.destroy = abstractMethod; - -/****************************************************************************** - * Bodies - */ - -/* @param {CANNON.Body} body */ -Driver.prototype.addBody = abstractMethod; - -/* @param {CANNON.Body} body */ -Driver.prototype.removeBody = abstractMethod; - -/** - * @param {CANNON.Body} body - * @param {string} methodName - * @param {Array} args - */ -Driver.prototype.applyBodyMethod = abstractMethod; - -/** @param {CANNON.Body} body */ -Driver.prototype.updateBodyProperties = abstractMethod; - -/****************************************************************************** - * Materials - */ - -/** @param {object} materialConfig */ -Driver.prototype.addMaterial = abstractMethod; - -/** - * @param {string} materialName1 - * @param {string} materialName2 - * @param {object} contactMaterialConfig - */ -Driver.prototype.addContactMaterial = abstractMethod; - -/****************************************************************************** - * Constraints - */ - -/* @param {CANNON.Constraint} constraint */ -Driver.prototype.addConstraint = abstractMethod; - -/* @param {CANNON.Constraint} constraint */ -Driver.prototype.removeConstraint = abstractMethod; - -/****************************************************************************** - * Contacts - */ - -/** @return {Array} */ -Driver.prototype.getContacts = abstractMethod; - -/*****************************************************************************/ - -function abstractMethod () { - throw new Error('Method not implemented.'); -} + if (this.statsToConsole || this.statsToEvents || this.statsToPanel) { + this.trackPerf = true; + this.tickCounter = 0; + + this.statsTickData = {}; + this.statsBodyData = {}; -},{}],23:[function(require,module,exports){ -module.exports = { - INIT: 'init', - STEP: 'step', - - // Bodies. - ADD_BODY: 'add-body', - REMOVE_BODY: 'remove-body', - APPLY_BODY_METHOD: 'apply-body-method', - UPDATE_BODY_PROPERTIES: 'update-body-properties', - - // Materials. - ADD_MATERIAL: 'add-material', - ADD_CONTACT_MATERIAL: 'add-contact-material', - - // Constraints. - ADD_CONSTRAINT: 'add-constraint', - REMOVE_CONSTRAINT: 'remove-constraint', - - // Events. - COLLIDE: 'collide' -}; + this.countBodies = { + "ammo": () => this.countBodiesAmmo(), + "local": () => this.countBodiesCannon(false), + "worker": () => this.countBodiesCannon(true) + } -},{}],24:[function(require,module,exports){ -var CANNON = require('cannon-es'), - Driver = require('./driver'); - -function LocalDriver () { - this.world = null; - this.materials = {}; - this.contactMaterial = null; -} - -LocalDriver.prototype = new Driver(); -LocalDriver.prototype.constructor = LocalDriver; - -module.exports = LocalDriver; - -/****************************************************************************** - * Lifecycle - */ - -/* @param {object} worldConfig */ -LocalDriver.prototype.init = function (worldConfig) { - var world = new CANNON.World(); - world.quatNormalizeSkip = worldConfig.quatNormalizeSkip; - world.quatNormalizeFast = worldConfig.quatNormalizeFast; - world.solver.iterations = worldConfig.solverIterations; - world.gravity.set(0, worldConfig.gravity, 0); - world.broadphase = new CANNON.NaiveBroadphase(); - - this.world = world; -}; - -/* @param {number} deltaMS */ -LocalDriver.prototype.step = function (deltaMS) { - this.world.step(deltaMS); -}; - -LocalDriver.prototype.destroy = function () { - delete this.world; - delete this.contactMaterial; - this.materials = {}; -}; - -/****************************************************************************** - * Bodies - */ - -/* @param {CANNON.Body} body */ -LocalDriver.prototype.addBody = function (body) { - this.world.addBody(body); -}; - -/* @param {CANNON.Body} body */ -LocalDriver.prototype.removeBody = function (body) { - this.world.removeBody(body); -}; - -/** - * @param {CANNON.Body} body - * @param {string} methodName - * @param {Array} args - */ -LocalDriver.prototype.applyBodyMethod = function (body, methodName, args) { - body['__' + methodName].apply(body, args); -}; - -/** @param {CANNON.Body} body */ -LocalDriver.prototype.updateBodyProperties = function () {}; - -/****************************************************************************** - * Materials - */ - -/** - * @param {string} name - * @return {CANNON.Material} - */ -LocalDriver.prototype.getMaterial = function (name) { - return this.materials[name]; -}; - -/** @param {object} materialConfig */ -LocalDriver.prototype.addMaterial = function (materialConfig) { - this.materials[materialConfig.name] = new CANNON.Material(materialConfig); - this.materials[materialConfig.name].name = materialConfig.name; -}; - -/** - * @param {string} matName1 - * @param {string} matName2 - * @param {object} contactMaterialConfig - */ -LocalDriver.prototype.addContactMaterial = function (matName1, matName2, contactMaterialConfig) { - var mat1 = this.materials[matName1], - mat2 = this.materials[matName2]; - this.contactMaterial = new CANNON.ContactMaterial(mat1, mat2, contactMaterialConfig); - this.world.addContactMaterial(this.contactMaterial); -}; - -/****************************************************************************** - * Constraints - */ - -/* @param {CANNON.Constraint} constraint */ -LocalDriver.prototype.addConstraint = function (constraint) { - if (!constraint.type) { - if (constraint instanceof CANNON.LockConstraint) { - constraint.type = 'LockConstraint'; - } else if (constraint instanceof CANNON.DistanceConstraint) { - constraint.type = 'DistanceConstraint'; - } else if (constraint instanceof CANNON.HingeConstraint) { - constraint.type = 'HingeConstraint'; - } else if (constraint instanceof CANNON.ConeTwistConstraint) { - constraint.type = 'ConeTwistConstraint'; - } else if (constraint instanceof CANNON.PointToPointConstraint) { - constraint.type = 'PointToPointConstraint'; - } - } - this.world.addConstraint(constraint); -}; - -/* @param {CANNON.Constraint} constraint */ -LocalDriver.prototype.removeConstraint = function (constraint) { - this.world.removeConstraint(constraint); -}; - -/****************************************************************************** - * Contacts - */ - -/** @return {Array} */ -LocalDriver.prototype.getContacts = function () { - return this.world.contacts; -}; + this.bodyTypeToStatsPropertyMap = { + "ammo": { + [TYPE.STATIC] : "staticBodies", + [TYPE.KINEMATIC] : "kinematicBodies", + [TYPE.DYNAMIC] : "dynamicBodies", + }, + "cannon": { + [CANNON.Body.STATIC] : "staticBodies", + [CANNON.Body.DYNAMIC] : "dynamicBodies" + } + } + + const scene = this.el.sceneEl; + scene.setAttribute("stats-collector", `inEvent: physics-tick-data; + properties: before, after, engine, total; + outputFrequency: 100; + outEvent: physics-tick-summary; + outputs: percentile__50, percentile__90, max`); + } + + if (this.statsToPanel) { + const scene = this.el.sceneEl; + const space = "   " + + scene.setAttribute("stats-panel", "") + scene.setAttribute("stats-group__bodies", `label: Physics Bodies`) + scene.setAttribute("stats-row__b1", `group: bodies; + event:physics-body-data; + properties: staticBodies; + label: Static`) + scene.setAttribute("stats-row__b2", `group: bodies; + event:physics-body-data; + properties: dynamicBodies; + label: Dynamic`) + if (this.data.driver === 'local' || this.data.driver === 'worker') { + scene.setAttribute("stats-row__b3", `group: bodies; + event:physics-body-data; + properties: contacts; + label: Contacts`) + } + else if (this.data.driver === 'ammo') { + scene.setAttribute("stats-row__b3", `group: bodies; + event:physics-body-data; + properties: kinematicBodies; + label: Kinematic`) + scene.setAttribute("stats-row__b4", `group: bodies; + event: physics-body-data; + properties: manifolds; + label: Manifolds`) + scene.setAttribute("stats-row__b5", `group: bodies; + event: physics-body-data; + properties: manifoldContacts; + label: Contacts`) + scene.setAttribute("stats-row__b6", `group: bodies; + event: physics-body-data; + properties: collisions; + label: Collisions`) + scene.setAttribute("stats-row__b7", `group: bodies; + event: physics-body-data; + properties: collisionKeys; + label: Coll Keys`) + } -},{"./driver":22,"cannon-es":5}],25:[function(require,module,exports){ -var Driver = require('./driver'); - -function NetworkDriver () { - throw new Error('[NetworkDriver] Driver not implemented.'); -} - -NetworkDriver.prototype = new Driver(); -NetworkDriver.prototype.constructor = NetworkDriver; - -module.exports = NetworkDriver; + scene.setAttribute("stats-group__tick", `label: Physics Ticks: Median${space}90th%${space}99th%`) + scene.setAttribute("stats-row__1", `group: tick; + event:physics-tick-summary; + properties: before.percentile__50, + before.percentile__90, + before.max; + label: Before`) + scene.setAttribute("stats-row__2", `group: tick; + event:physics-tick-summary; + properties: after.percentile__50, + after.percentile__90, + after.max; + label: After`) + scene.setAttribute("stats-row__3", `group: tick; + event:physics-tick-summary; + properties: engine.percentile__50, + engine.percentile__90, + engine.max; + label: Engine`) + scene.setAttribute("stats-row__4", `group: tick; + event:physics-tick-summary; + properties: total.percentile__50, + total.percentile__90, + total.max; + label: Total`) + } + }, -},{"./driver":22}],26:[function(require,module,exports){ -/** - * Stub version of webworkify, for debugging code outside of a webworker. - */ -function webworkifyDebug (worker) { - var targetA = new EventTarget(), - targetB = new EventTarget(); - - targetA.setTarget(targetB); - targetB.setTarget(targetA); - - worker(targetA); - return targetB; -} - -module.exports = webworkifyDebug; - -/****************************************************************************** - * EventTarget - */ - -function EventTarget () { - this.listeners = []; -} - -EventTarget.prototype.setTarget = function (target) { - this.target = target; -}; - -EventTarget.prototype.addEventListener = function (type, fn) { - this.listeners.push(fn); -}; - -EventTarget.prototype.dispatchEvent = function (type, event) { - for (var i = 0; i < this.listeners.length; i++) { - this.listeners[i](event); - } -}; - -EventTarget.prototype.postMessage = function (msg) { - this.target.dispatchEvent('message', {data: msg}); -}; + /** + * Updates the physics world on each tick of the A-Frame scene. It would be + * entirely possible to separate the two – updating physics more or less + * frequently than the scene – if greater precision or performance were + * necessary. + * @param {number} t + * @param {number} dt + */ + tick: function (t, dt) { + if (!this.initialized || !dt) return; -},{}],27:[function(require,module,exports){ -/* global performance */ - -var webworkify = require('webworkify'), - webworkifyDebug = require('./webworkify-debug'), - Driver = require('./driver'), - Event = require('./event'), - worker = require('./worker'), - protocol = require('../utils/protocol'); - -var ID = protocol.ID; - -/****************************************************************************** - * Constructor - */ - -function WorkerDriver (options) { - this.fps = options.fps; - this.engine = options.engine; - this.interpolate = options.interpolate; - // Approximate number of physics steps to 'pad' rendering. - this.interpBufferSize = options.interpolationBufferSize; - this.debug = options.debug; - - this.bodies = {}; - this.contacts = []; - - // https://gafferongames.com/post/snapshot_interpolation/ - this.frameDelay = this.interpBufferSize * 1000 / this.fps; - this.frameBuffer = []; - - this.worker = this.debug - ? webworkifyDebug(worker) - : webworkify(worker); - this.worker.addEventListener('message', this._onMessage.bind(this)); -} - -WorkerDriver.prototype = new Driver(); -WorkerDriver.prototype.constructor = WorkerDriver; - -module.exports = WorkerDriver; - -/****************************************************************************** - * Lifecycle - */ - -/* @param {object} worldConfig */ -WorkerDriver.prototype.init = function (worldConfig) { - this.worker.postMessage({ - type: Event.INIT, - worldConfig: worldConfig, - fps: this.fps, - engine: this.engine - }); -}; - -/** - * Increments the physics world forward one step, if interpolation is enabled. - * If disabled, increments are performed as messages arrive. - * @param {number} deltaMS - */ -WorkerDriver.prototype.step = function () { - if (!this.interpolate) return; - - // Get the two oldest frames that haven't expired. Ideally we would use all - // available frames to keep things smooth, but lerping is easier and faster. - var prevFrame = this.frameBuffer[0]; - var nextFrame = this.frameBuffer[1]; - var timestamp = performance.now(); - while (prevFrame && nextFrame && timestamp - prevFrame.timestamp > this.frameDelay) { - this.frameBuffer.shift(); - prevFrame = this.frameBuffer[0]; - nextFrame = this.frameBuffer[1]; - } - - if (!prevFrame || !nextFrame) return; - - var mix = (timestamp - prevFrame.timestamp) / this.frameDelay; - mix = (mix - (1 - 1 / this.interpBufferSize)) * this.interpBufferSize; - - for (var id in prevFrame.bodies) { - if (prevFrame.bodies.hasOwnProperty(id) && nextFrame.bodies.hasOwnProperty(id)) { - protocol.deserializeInterpBodyUpdate( - prevFrame.bodies[id], - nextFrame.bodies[id], - this.bodies[id], - mix - ); - } - } -}; - -WorkerDriver.prototype.destroy = function () { - this.worker.terminate(); - delete this.worker; -}; - -/** {Event} event */ -WorkerDriver.prototype._onMessage = function (event) { - if (event.data.type === Event.STEP) { - var data = event.data, - bodies = data.bodies; - - this.contacts = event.data.contacts; - - // If interpolation is enabled, store the frame. If not, update all bodies - // immediately. - if (this.interpolate) { - this.frameBuffer.push({timestamp: performance.now(), bodies: bodies}); - } else { - for (var id in bodies) { - if (bodies.hasOwnProperty(id)) { - protocol.deserializeBodyUpdate(bodies[id], this.bodies[id]); - } - } - } - - } else if (event.data.type === Event.COLLIDE) { - var body = this.bodies[event.data.bodyID]; - var target = this.bodies[event.data.targetID]; - var contact = protocol.deserializeContact(event.data.contact, this.bodies); - if (!body._listeners || !body._listeners.collide) return; - for (var i = 0; i < body._listeners.collide.length; i++) { - body._listeners.collide[i]({target: target, body: body, contact: contact}); - } - - } else { - throw new Error('[WorkerDriver] Unexpected message type.'); - } -}; - -/****************************************************************************** - * Bodies - */ - -/* @param {CANNON.Body} body */ -WorkerDriver.prototype.addBody = function (body) { - protocol.assignID('body', body); - this.bodies[body[ID]] = body; - this.worker.postMessage({type: Event.ADD_BODY, body: protocol.serializeBody(body)}); -}; - -/* @param {CANNON.Body} body */ -WorkerDriver.prototype.removeBody = function (body) { - this.worker.postMessage({type: Event.REMOVE_BODY, bodyID: body[ID]}); - delete this.bodies[body[ID]]; -}; - -/** - * @param {CANNON.Body} body - * @param {string} methodName - * @param {Array} args - */ -WorkerDriver.prototype.applyBodyMethod = function (body, methodName, args) { - switch (methodName) { - case 'applyForce': - case 'applyImpulse': - this.worker.postMessage({ - type: Event.APPLY_BODY_METHOD, - bodyID: body[ID], - methodName: methodName, - args: [args[0].toArray(), args[1].toArray()] - }); - break; - default: - throw new Error('Unexpected methodName: %s', methodName); - } -}; - -/** @param {CANNON.Body} body */ -WorkerDriver.prototype.updateBodyProperties = function (body) { - this.worker.postMessage({ - type: Event.UPDATE_BODY_PROPERTIES, - body: protocol.serializeBody(body) - }); -}; - -/****************************************************************************** - * Materials - */ - -/** - * @param {string} name - * @return {CANNON.Material} - */ -WorkerDriver.prototype.getMaterial = function (name) { - // No access to materials here. Eventually we might return the name or ID, if - // multiple materials were selected, but for now there's only one and it's safe - // to assume the worker is already using it. - return undefined; -}; - -/** @param {object} materialConfig */ -WorkerDriver.prototype.addMaterial = function (materialConfig) { - this.worker.postMessage({type: Event.ADD_MATERIAL, materialConfig: materialConfig}); -}; - -/** - * @param {string} matName1 - * @param {string} matName2 - * @param {object} contactMaterialConfig - */ -WorkerDriver.prototype.addContactMaterial = function (matName1, matName2, contactMaterialConfig) { - this.worker.postMessage({ - type: Event.ADD_CONTACT_MATERIAL, - materialName1: matName1, - materialName2: matName2, - contactMaterialConfig: contactMaterialConfig - }); -}; - -/****************************************************************************** - * Constraints - */ - -/* @param {CANNON.Constraint} constraint */ -WorkerDriver.prototype.addConstraint = function (constraint) { - if (!constraint.type) { - if (constraint instanceof CANNON.LockConstraint) { - constraint.type = 'LockConstraint'; - } else if (constraint instanceof CANNON.DistanceConstraint) { - constraint.type = 'DistanceConstraint'; - } else if (constraint instanceof CANNON.HingeConstraint) { - constraint.type = 'HingeConstraint'; - } else if (constraint instanceof CANNON.ConeTwistConstraint) { - constraint.type = 'ConeTwistConstraint'; - } else if (constraint instanceof CANNON.PointToPointConstraint) { - constraint.type = 'PointToPointConstraint'; - } - } - protocol.assignID('constraint', constraint); - this.worker.postMessage({ - type: Event.ADD_CONSTRAINT, - constraint: protocol.serializeConstraint(constraint) - }); -}; - -/* @param {CANNON.Constraint} constraint */ -WorkerDriver.prototype.removeConstraint = function (constraint) { - this.worker.postMessage({ - type: Event.REMOVE_CONSTRAINT, - constraintID: constraint[ID] - }); -}; - -/****************************************************************************** - * Contacts - */ - -/** @return {Array} */ -WorkerDriver.prototype.getContacts = function () { - // TODO(donmccurdy): There's some wasted memory allocation here. - var bodies = this.bodies; - return this.contacts.map(function (message) { - return protocol.deserializeContact(message, bodies); - }); -}; + const beforeStartTime = performance.now(); -},{"../utils/protocol":31,"./driver":22,"./event":23,"./webworkify-debug":26,"./worker":28,"webworkify":8}],28:[function(require,module,exports){ -var Event = require('./event'), - LocalDriver = require('./local-driver'), - AmmoDriver = require('./ammo-driver'), - protocol = require('../utils/protocol'); - -var ID = protocol.ID; - -module.exports = function (self) { - var driver = null; - var bodies = {}; - var constraints = {}; - var stepSize; - - self.addEventListener('message', function (event) { - var data = event.data; - - switch (data.type) { - // Lifecycle. - case Event.INIT: - driver = data.engine === 'cannon' - ? new LocalDriver() - : new AmmoDriver(); - driver.init(data.worldConfig); - stepSize = 1 / data.fps; - setInterval(step, 1000 / data.fps); - break; - - // Bodies. - case Event.ADD_BODY: - var body = protocol.deserializeBody(data.body); - body.material = driver.getMaterial( 'defaultMaterial' ); - bodies[body[ID]] = body; - - body.addEventListener('collide', function (evt) { - var message = { - type: Event.COLLIDE, - bodyID: evt.target[ID], // set the target as the body to be identical to the local driver - targetID: evt.body[ID], // set the body as the target to be identical to the local driver - contact: protocol.serializeContact(evt.contact) - } - self.postMessage(message); - }); - driver.addBody(body); - break; - case Event.REMOVE_BODY: - driver.removeBody(bodies[data.bodyID]); - delete bodies[data.bodyID]; - break; - case Event.APPLY_BODY_METHOD: - bodies[data.bodyID][data.methodName]( - protocol.deserializeVec3(data.args[0]), - protocol.deserializeVec3(data.args[1]) - ); - break; - case Event.UPDATE_BODY_PROPERTIES: - protocol.deserializeBodyUpdate(data.body, bodies[data.body.id]); - break; - - // Materials. - case Event.ADD_MATERIAL: - driver.addMaterial(data.materialConfig); - break; - case Event.ADD_CONTACT_MATERIAL: - driver.addContactMaterial( - data.materialName1, - data.materialName2, - data.contactMaterialConfig - ); - break; - - // Constraints. - case Event.ADD_CONSTRAINT: - var constraint = protocol.deserializeConstraint(data.constraint, bodies); - constraints[constraint[ID]] = constraint; - driver.addConstraint(constraint); - break; - case Event.REMOVE_CONSTRAINT: - driver.removeConstraint(constraints[data.constraintID]); - delete constraints[data.constraintID]; - break; - - default: - throw new Error('[Worker] Unexpected event type: %s', data.type); - - } - }); - - function step () { - driver.step(stepSize); - - var bodyMessages = {}; - Object.keys(bodies).forEach(function (id) { - bodyMessages[id] = protocol.serializeBody(bodies[id]); - }); - - self.postMessage({ - type: Event.STEP, - bodies: bodyMessages, - contacts: driver.getContacts().map(protocol.serializeContact) - }); - } -}; + var i; + var callbacks = this.callbacks; -},{"../utils/protocol":31,"./ammo-driver":21,"./event":23,"./local-driver":24}],29:[function(require,module,exports){ -/* global THREE */ -var CANNON = require('cannon-es'), - CONSTANTS = require('./constants'), - C_GRAV = CONSTANTS.GRAVITY, - C_MAT = CONSTANTS.CONTACT_MATERIAL; - -const { TYPE } = require('./constants'); -var LocalDriver = require('./drivers/local-driver'), - WorkerDriver = require('./drivers/worker-driver'), - NetworkDriver = require('./drivers/network-driver'), - AmmoDriver = require('./drivers/ammo-driver'); -require('aframe-stats-panel') - -/** - * Physics system. - */ -module.exports = AFRAME.registerSystem('physics', { - schema: { - // CANNON.js driver type - driver: { default: 'local', oneOf: ['local', 'worker', 'network', 'ammo'] }, - networkUrl: { default: '', if: {driver: 'network'} }, - workerFps: { default: 60, if: {driver: 'worker'} }, - workerInterpolate: { default: true, if: {driver: 'worker'} }, - workerInterpBufferSize: { default: 2, if: {driver: 'worker'} }, - workerEngine: { default: 'cannon', if: {driver: 'worker'}, oneOf: ['cannon'] }, - workerDebug: { default: false, if: {driver: 'worker'} }, - - gravity: { default: C_GRAV }, - iterations: { default: CONSTANTS.ITERATIONS }, - friction: { default: C_MAT.friction }, - restitution: { default: C_MAT.restitution }, - contactEquationStiffness: { default: C_MAT.contactEquationStiffness }, - contactEquationRelaxation: { default: C_MAT.contactEquationRelaxation }, - frictionEquationStiffness: { default: C_MAT.frictionEquationStiffness }, - frictionEquationRegularization: { default: C_MAT.frictionEquationRegularization }, - - // Never step more than four frames at once. Effectively pauses the scene - // when out of focus, and prevents weird "jumps" when focus returns. - maxInterval: { default: 4 / 60 }, - - // If true, show wireframes around physics bodies. - debug: { default: false }, - - // If using ammo, set the default rendering mode for debug - debugDrawMode: { default: THREE.AmmoDebugConstants.NoDebug }, - // If using ammo, set the max number of steps per frame - maxSubSteps: { default: 4 }, - // If using ammo, set the framerate of the simulation - fixedTimeStep: { default: 1 / 60 }, - // Whether to output stats, and how to output them. One or more of "console", "events", "panel" - stats: {type: 'array', default: []} - }, - - /** - * Initializes the physics system. - */ - async init() { - var data = this.data; - - // If true, show wireframes around physics bodies. - this.debug = data.debug; - this.initStats(); - - this.callbacks = {beforeStep: [], step: [], afterStep: []}; - - this.listeners = {}; - - - this.driver = null; - switch (data.driver) { - case 'local': - this.driver = new LocalDriver(); - break; - - case 'ammo': - this.driver = new AmmoDriver(); - break; - - case 'network': - this.driver = new NetworkDriver(data.networkUrl); - break; - - case 'worker': - this.driver = new WorkerDriver({ - fps: data.workerFps, - engine: data.workerEngine, - interpolate: data.workerInterpolate, - interpolationBufferSize: data.workerInterpBufferSize, - debug: data.workerDebug - }); - break; - - default: - throw new Error('[physics] Driver not recognized: "%s".', data.driver); - } - - if (data.driver !== 'ammo') { - await this.driver.init({ - quatNormalizeSkip: 0, - quatNormalizeFast: false, - solverIterations: data.iterations, - gravity: data.gravity, - }); - this.driver.addMaterial({name: 'defaultMaterial'}); - this.driver.addMaterial({name: 'staticMaterial'}); - this.driver.addContactMaterial('defaultMaterial', 'defaultMaterial', { - friction: data.friction, - restitution: data.restitution, - contactEquationStiffness: data.contactEquationStiffness, - contactEquationRelaxation: data.contactEquationRelaxation, - frictionEquationStiffness: data.frictionEquationStiffness, - frictionEquationRegularization: data.frictionEquationRegularization - }); - this.driver.addContactMaterial('staticMaterial', 'defaultMaterial', { - friction: 1.0, - restitution: 0.0, - contactEquationStiffness: data.contactEquationStiffness, - contactEquationRelaxation: data.contactEquationRelaxation, - frictionEquationStiffness: data.frictionEquationStiffness, - frictionEquationRegularization: data.frictionEquationRegularization - }); - } else { - await this.driver.init({ - gravity: data.gravity, - debugDrawMode: data.debugDrawMode, - solverIterations: data.iterations, - maxSubSteps: data.maxSubSteps, - fixedTimeStep: data.fixedTimeStep - }); - } - - this.initialized = true; - - if (this.debug) { - this.setDebug(true); - } - }, - - initStats() { - // Data used for performance monitoring. - this.statsToConsole = this.data.stats.includes("console") - this.statsToEvents = this.data.stats.includes("events") - this.statsToPanel = this.data.stats.includes("panel") - - if (this.statsToConsole || this.statsToEvents || this.statsToPanel) { - this.trackPerf = true; - this.tickCounter = 0; - - this.statsTickData = {}; - this.statsBodyData = {}; - - this.countBodies = { - "ammo": () => this.countBodiesAmmo(), - "local": () => this.countBodiesCannon(false), - "worker": () => this.countBodiesCannon(true) - } - - this.bodyTypeToStatsPropertyMap = { - "ammo": { - [TYPE.STATIC] : "staticBodies", - [TYPE.KINEMATIC] : "kinematicBodies", - [TYPE.DYNAMIC] : "dynamicBodies", - }, - "cannon": { - [CANNON.Body.STATIC] : "staticBodies", - [CANNON.Body.DYNAMIC] : "dynamicBodies" - } - } - - const scene = this.el.sceneEl; - scene.setAttribute("stats-collector", `inEvent: physics-tick-data; - properties: before, after, engine, total; - outputFrequency: 100; - outEvent: physics-tick-summary; - outputs: percentile__50, percentile__90, max`); - } - - if (this.statsToPanel) { - const scene = this.el.sceneEl; - const space = "   " - - scene.setAttribute("stats-panel", "") - scene.setAttribute("stats-group__bodies", `label: Physics Bodies`) - scene.setAttribute("stats-row__b1", `group: bodies; - event:physics-body-data; - properties: staticBodies; - label: Static`) - scene.setAttribute("stats-row__b2", `group: bodies; - event:physics-body-data; - properties: dynamicBodies; - label: Dynamic`) - if (this.data.driver === 'local' || this.data.driver === 'worker') { - scene.setAttribute("stats-row__b3", `group: bodies; - event:physics-body-data; - properties: contacts; - label: Contacts`) - } - else if (this.data.driver === 'ammo') { - scene.setAttribute("stats-row__b3", `group: bodies; - event:physics-body-data; - properties: kinematicBodies; - label: Kinematic`) - scene.setAttribute("stats-row__b4", `group: bodies; - event: physics-body-data; - properties: manifolds; - label: Manifolds`) - scene.setAttribute("stats-row__b5", `group: bodies; - event: physics-body-data; - properties: manifoldContacts; - label: Contacts`) - scene.setAttribute("stats-row__b6", `group: bodies; - event: physics-body-data; - properties: collisions; - label: Collisions`) - scene.setAttribute("stats-row__b7", `group: bodies; - event: physics-body-data; - properties: collisionKeys; - label: Coll Keys`) - } - - scene.setAttribute("stats-group__tick", `label: Physics Ticks: Median${space}90th%${space}99th%`) - scene.setAttribute("stats-row__1", `group: tick; - event:physics-tick-summary; - properties: before.percentile__50, - before.percentile__90, - before.max; - label: Before`) - scene.setAttribute("stats-row__2", `group: tick; - event:physics-tick-summary; - properties: after.percentile__50, - after.percentile__90, - after.max; - label: After`) - scene.setAttribute("stats-row__3", `group: tick; - event:physics-tick-summary; - properties: engine.percentile__50, - engine.percentile__90, - engine.max; - label: Engine`) - scene.setAttribute("stats-row__4", `group: tick; - event:physics-tick-summary; - properties: total.percentile__50, - total.percentile__90, - total.max; - label: Total`) - } - }, - - /** - * Updates the physics world on each tick of the A-Frame scene. It would be - * entirely possible to separate the two – updating physics more or less - * frequently than the scene – if greater precision or performance were - * necessary. - * @param {number} t - * @param {number} dt - */ - tick: function (t, dt) { - if (!this.initialized || !dt) return; - - const beforeStartTime = performance.now(); - - var i; - var callbacks = this.callbacks; - - for (i = 0; i < this.callbacks.beforeStep.length; i++) { - this.callbacks.beforeStep[i].beforeStep(t, dt); - } - - const engineStartTime = performance.now(); - - this.driver.step(Math.min(dt / 1000, this.data.maxInterval)); - - const engineEndTime = performance.now(); - - for (i = 0; i < callbacks.step.length; i++) { - callbacks.step[i].step(t, dt); - } - - for (i = 0; i < callbacks.afterStep.length; i++) { - callbacks.afterStep[i].afterStep(t, dt); - } - - if (this.trackPerf) { - const afterEndTime = performance.now(); - - this.statsTickData.before = engineStartTime - beforeStartTime - this.statsTickData.engine = engineEndTime - engineStartTime - this.statsTickData.after = afterEndTime - engineEndTime - this.statsTickData.total = afterEndTime - beforeStartTime - - this.el.emit("physics-tick-data", this.statsTickData) - - this.tickCounter++; - - if (this.tickCounter === 100) { - - this.countBodies[this.data.driver]() - - if (this.statsToConsole) { - console.log("Physics body stats:", this.statsBodyData) - } - - if (this.statsToEvents || this.statsToPanel) { - this.el.emit("physics-body-data", this.statsBodyData) - } - this.tickCounter = 0; - } - } - }, - - countBodiesAmmo() { - - const statsData = this.statsBodyData - statsData.manifolds = this.driver.dispatcher.getNumManifolds(); - statsData.manifoldContacts = 0; - for (let i = 0; i < statsData.manifolds; i++) { - const manifold = this.driver.dispatcher.getManifoldByIndexInternal(i); - statsData.manifoldContacts += manifold.getNumContacts(); - } - statsData.collisions = this.driver.collisions.size; - statsData.collisionKeys = this.driver.collisionKeys.length; - statsData.staticBodies = 0 - statsData.kinematicBodies = 0 - statsData.dynamicBodies = 0 - - function type(el) { - return el.components['ammo-body'].data.type - } - - this.driver.els.forEach((el) => { - const property = this.bodyTypeToStatsPropertyMap["ammo"][type(el)] - statsData[property]++ - }) - }, - - countBodiesCannon(worker) { - - const statsData = this.statsBodyData - statsData.contacts = worker ? this.driver.contacts.length : this.driver.world.contacts.length; - statsData.staticBodies = 0 - statsData.dynamicBodies = 0 - - const bodies = worker ? Object.values(this.driver.bodies) : this.driver.world.bodies - - bodies.forEach((body) => { - const property = this.bodyTypeToStatsPropertyMap["cannon"][body.type] - statsData[property]++ - }) - }, - - setDebug: function(debug) { - this.debug = debug; - if (this.data.driver === 'ammo' && this.initialized) { - if (debug && !this.debugDrawer) { - this.debugDrawer = this.driver.getDebugDrawer(this.el.object3D); - this.debugDrawer.enable(); - } else if (this.debugDrawer) { - this.debugDrawer.disable(); - this.debugDrawer = null; - } - } - }, - - /** - * Adds a body to the scene, and binds proxied methods to the driver. - * @param {CANNON.Body} body - */ - addBody: function (body, group, mask) { - var driver = this.driver; - - if (this.data.driver === 'local') { - body.__applyImpulse = body.applyImpulse; - body.applyImpulse = function () { - driver.applyBodyMethod(body, 'applyImpulse', arguments); - }; - - body.__applyForce = body.applyForce; - body.applyForce = function () { - driver.applyBodyMethod(body, 'applyForce', arguments); - }; - - body.updateProperties = function () { - driver.updateBodyProperties(body); - }; - - this.listeners[body.id] = function (e) { body.el.emit('collide', e); }; - body.addEventListener('collide', this.listeners[body.id]); - } - - this.driver.addBody(body, group, mask); - }, - - /** - * Removes a body and its proxied methods. - * @param {CANNON.Body} body - */ - removeBody: function (body) { - this.driver.removeBody(body); - - if (this.data.driver === 'local' || this.data.driver === 'worker') { - body.removeEventListener('collide', this.listeners[body.id]); - delete this.listeners[body.id]; - - body.applyImpulse = body.__applyImpulse; - delete body.__applyImpulse; - - body.applyForce = body.__applyForce; - delete body.__applyForce; - - delete body.updateProperties; - } - }, - - /** @param {CANNON.Constraint or Ammo.btTypedConstraint} constraint */ - addConstraint: function (constraint) { - this.driver.addConstraint(constraint); - }, - - /** @param {CANNON.Constraint or Ammo.btTypedConstraint} constraint */ - removeConstraint: function (constraint) { - this.driver.removeConstraint(constraint); - }, - - /** - * Adds a component instance to the system and schedules its update methods to be called - * the given phase. - * @param {Component} component - * @param {string} phase - */ - addComponent: function (component) { - var callbacks = this.callbacks; - if (component.beforeStep) callbacks.beforeStep.push(component); - if (component.step) callbacks.step.push(component); - if (component.afterStep) callbacks.afterStep.push(component); - }, - - /** - * Removes a component instance from the system. - * @param {Component} component - * @param {string} phase - */ - removeComponent: function (component) { - var callbacks = this.callbacks; - if (component.beforeStep) { - callbacks.beforeStep.splice(callbacks.beforeStep.indexOf(component), 1); - } - if (component.step) { - callbacks.step.splice(callbacks.step.indexOf(component), 1); - } - if (component.afterStep) { - callbacks.afterStep.splice(callbacks.afterStep.indexOf(component), 1); - } - }, - - /** @return {Array} */ - getContacts: function () { - return this.driver.getContacts(); - }, - - getMaterial: function (name) { - return this.driver.getMaterial(name); - } -}); + for (i = 0; i < this.callbacks.beforeStep.length; i++) { + this.callbacks.beforeStep[i].beforeStep(t, dt); + } + + const engineStartTime = performance.now(); + + this.driver.step(Math.min(dt / 1000, this.data.maxInterval)); + + const engineEndTime = performance.now(); + + for (i = 0; i < callbacks.step.length; i++) { + callbacks.step[i].step(t, dt); + } + + for (i = 0; i < callbacks.afterStep.length; i++) { + callbacks.afterStep[i].afterStep(t, dt); + } + + if (this.trackPerf) { + const afterEndTime = performance.now(); + + this.statsTickData.before = engineStartTime - beforeStartTime + this.statsTickData.engine = engineEndTime - engineStartTime + this.statsTickData.after = afterEndTime - engineEndTime + this.statsTickData.total = afterEndTime - beforeStartTime + + this.el.emit("physics-tick-data", this.statsTickData) + + this.tickCounter++; + + if (this.tickCounter === 100) { + + this.countBodies[this.data.driver]() + + if (this.statsToConsole) { + console.log("Physics body stats:", this.statsBodyData) + } + + if (this.statsToEvents || this.statsToPanel) { + this.el.emit("physics-body-data", this.statsBodyData) + } + this.tickCounter = 0; + } + } + }, + + countBodiesAmmo() { + + const statsData = this.statsBodyData + statsData.manifolds = this.driver.dispatcher.getNumManifolds(); + statsData.manifoldContacts = 0; + for (let i = 0; i < statsData.manifolds; i++) { + const manifold = this.driver.dispatcher.getManifoldByIndexInternal(i); + statsData.manifoldContacts += manifold.getNumContacts(); + } + statsData.collisions = this.driver.collisions.size; + statsData.collisionKeys = this.driver.collisionKeys.length; + statsData.staticBodies = 0 + statsData.kinematicBodies = 0 + statsData.dynamicBodies = 0 + + function type(el) { + return el.components['ammo-body'].data.type + } + + this.driver.els.forEach((el) => { + const property = this.bodyTypeToStatsPropertyMap["ammo"][type(el)] + statsData[property]++ + }) + }, + + countBodiesCannon(worker) { + + const statsData = this.statsBodyData + statsData.contacts = worker ? this.driver.contacts.length : this.driver.world.contacts.length; + statsData.staticBodies = 0 + statsData.dynamicBodies = 0 + + const bodies = worker ? Object.values(this.driver.bodies) : this.driver.world.bodies + + bodies.forEach((body) => { + const property = this.bodyTypeToStatsPropertyMap["cannon"][body.type] + statsData[property]++ + }) + }, + + setDebug: function(debug) { + this.debug = debug; + if (this.data.driver === 'ammo' && this.initialized) { + if (debug && !this.debugDrawer) { + this.debugDrawer = this.driver.getDebugDrawer(this.el.object3D); + this.debugDrawer.enable(); + } else if (this.debugDrawer) { + this.debugDrawer.disable(); + this.debugDrawer = null; + } + } + }, + + /** + * Adds a body to the scene, and binds proxied methods to the driver. + * @param {CANNON.Body} body + */ + addBody: function (body, group, mask) { + var driver = this.driver; + + if (this.data.driver === 'local') { + body.__applyImpulse = body.applyImpulse; + body.applyImpulse = function () { + driver.applyBodyMethod(body, 'applyImpulse', arguments); + }; + + body.__applyForce = body.applyForce; + body.applyForce = function () { + driver.applyBodyMethod(body, 'applyForce', arguments); + }; + + body.updateProperties = function () { + driver.updateBodyProperties(body); + }; + + this.listeners[body.id] = function (e) { body.el.emit('collide', e); }; + body.addEventListener('collide', this.listeners[body.id]); + } + + this.driver.addBody(body, group, mask); + }, + + /** + * Removes a body and its proxied methods. + * @param {CANNON.Body} body + */ + removeBody: function (body) { + this.driver.removeBody(body); + + if (this.data.driver === 'local' || this.data.driver === 'worker') { + body.removeEventListener('collide', this.listeners[body.id]); + delete this.listeners[body.id]; + + body.applyImpulse = body.__applyImpulse; + delete body.__applyImpulse; + + body.applyForce = body.__applyForce; + delete body.__applyForce; + + delete body.updateProperties; + } + }, + + /** @param {CANNON.Constraint or Ammo.btTypedConstraint} constraint */ + addConstraint: function (constraint) { + this.driver.addConstraint(constraint); + }, + + /** @param {CANNON.Constraint or Ammo.btTypedConstraint} constraint */ + removeConstraint: function (constraint) { + this.driver.removeConstraint(constraint); + }, + + /** + * Adds a component instance to the system and schedules its update methods to be called + * the given phase. + * @param {Component} component + * @param {string} phase + */ + addComponent: function (component) { + var callbacks = this.callbacks; + if (component.beforeStep) callbacks.beforeStep.push(component); + if (component.step) callbacks.step.push(component); + if (component.afterStep) callbacks.afterStep.push(component); + }, + + /** + * Removes a component instance from the system. + * @param {Component} component + * @param {string} phase + */ + removeComponent: function (component) { + var callbacks = this.callbacks; + if (component.beforeStep) { + callbacks.beforeStep.splice(callbacks.beforeStep.indexOf(component), 1); + } + if (component.step) { + callbacks.step.splice(callbacks.step.indexOf(component), 1); + } + if (component.afterStep) { + callbacks.afterStep.splice(callbacks.afterStep.indexOf(component), 1); + } + }, + + /** @return {Array} */ + getContacts: function () { + return this.driver.getContacts(); + }, + + getMaterial: function (name) { + return this.driver.getMaterial(name); + } +}); },{"./constants":20,"./drivers/ammo-driver":21,"./drivers/local-driver":24,"./drivers/network-driver":25,"./drivers/worker-driver":27,"aframe-stats-panel":3,"cannon-es":5}],30:[function(require,module,exports){ -module.exports.slerp = function ( a, b, t ) { - if ( t <= 0 ) return a; - if ( t >= 1 ) return b; - - var x = a[0], y = a[1], z = a[2], w = a[3]; - - // https://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ - - var cosHalfTheta = w * b[3] + x * b[0] + y * b[1] + z * b[2]; - - if ( cosHalfTheta < 0 ) { - - a = a.slice(); - - a[3] = - b[3]; - a[0] = - b[0]; - a[1] = - b[1]; - a[2] = - b[2]; - - cosHalfTheta = - cosHalfTheta; - - } else { - - return b; - - } - - if ( cosHalfTheta >= 1.0 ) { - - a[3] = w; - a[0] = x; - a[1] = y; - a[2] = z; - - return this; - - } - - var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta ); - - if ( Math.abs( sinHalfTheta ) < 0.001 ) { - - a[3] = 0.5 * ( w + a[3] ); - a[0] = 0.5 * ( x + a[0] ); - a[1] = 0.5 * ( y + a[1] ); - a[2] = 0.5 * ( z + a[2] ); - - return this; - - } - - var halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta ); - var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta; - var ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; - - a[3] = ( w * ratioA + a[3] * ratioB ); - a[0] = ( x * ratioA + a[0] * ratioB ); - a[1] = ( y * ratioA + a[1] * ratioB ); - a[2] = ( z * ratioA + a[2] * ratioB ); - - return a; - -}; +module.exports.slerp = function ( a, b, t ) { + if ( t <= 0 ) return a; + if ( t >= 1 ) return b; + + var x = a[0], y = a[1], z = a[2], w = a[3]; + + // https://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ + + var cosHalfTheta = w * b[3] + x * b[0] + y * b[1] + z * b[2]; + + if ( cosHalfTheta < 0 ) { + + a = a.slice(); + + a[3] = - b[3]; + a[0] = - b[0]; + a[1] = - b[1]; + a[2] = - b[2]; + + cosHalfTheta = - cosHalfTheta; + + } else { + + return b; + + } + + if ( cosHalfTheta >= 1.0 ) { + + a[3] = w; + a[0] = x; + a[1] = y; + a[2] = z; + + return this; + + } + + var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta ); + + if ( Math.abs( sinHalfTheta ) < 0.001 ) { + + a[3] = 0.5 * ( w + a[3] ); + a[0] = 0.5 * ( x + a[0] ); + a[1] = 0.5 * ( y + a[1] ); + a[2] = 0.5 * ( z + a[2] ); + + return this; + + } + + var halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta ); + var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta; + var ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; + + a[3] = ( w * ratioA + a[3] * ratioB ); + a[0] = ( x * ratioA + a[0] * ratioB ); + a[1] = ( y * ratioA + a[1] * ratioB ); + a[2] = ( z * ratioA + a[2] * ratioB ); + + return a; + +}; },{}],31:[function(require,module,exports){ -var CANNON = require('cannon-es'); -var mathUtils = require('./math'); - -/****************************************************************************** - * IDs - */ - -var ID = '__id'; -module.exports.ID = ID; - -var nextID = {}; -module.exports.assignID = function (prefix, object) { - if (object[ID]) return; - nextID[prefix] = nextID[prefix] || 1; - object[ID] = prefix + '_' + nextID[prefix]++; -}; - -/****************************************************************************** - * Bodies - */ - -module.exports.serializeBody = function (body) { - var message = { - // Shapes. - shapes: body.shapes.map(serializeShape), - shapeOffsets: body.shapeOffsets.map(serializeVec3), - shapeOrientations: body.shapeOrientations.map(serializeQuaternion), - - // Vectors. - position: serializeVec3(body.position), - quaternion: body.quaternion.toArray(), - velocity: serializeVec3(body.velocity), - angularVelocity: serializeVec3(body.angularVelocity), - - // Properties. - id: body[ID], - mass: body.mass, - linearDamping: body.linearDamping, - angularDamping: body.angularDamping, - fixedRotation: body.fixedRotation, - allowSleep: body.allowSleep, - sleepSpeedLimit: body.sleepSpeedLimit, - sleepTimeLimit: body.sleepTimeLimit - }; - - return message; -}; - -module.exports.deserializeBodyUpdate = function (message, body) { - body.position.set(message.position[0], message.position[1], message.position[2]); - body.quaternion.set(message.quaternion[0], message.quaternion[1], message.quaternion[2], message.quaternion[3]); - body.velocity.set(message.velocity[0], message.velocity[1], message.velocity[2]); - body.angularVelocity.set(message.angularVelocity[0], message.angularVelocity[1], message.angularVelocity[2]); - - body.linearDamping = message.linearDamping; - body.angularDamping = message.angularDamping; - body.fixedRotation = message.fixedRotation; - body.allowSleep = message.allowSleep; - body.sleepSpeedLimit = message.sleepSpeedLimit; - body.sleepTimeLimit = message.sleepTimeLimit; - - if (body.mass !== message.mass) { - body.mass = message.mass; - body.updateMassProperties(); - } - - return body; -}; - -module.exports.deserializeInterpBodyUpdate = function (message1, message2, body, mix) { - var weight1 = 1 - mix; - var weight2 = mix; - - body.position.set( - message1.position[0] * weight1 + message2.position[0] * weight2, - message1.position[1] * weight1 + message2.position[1] * weight2, - message1.position[2] * weight1 + message2.position[2] * weight2 - ); - var quaternion = mathUtils.slerp(message1.quaternion, message2.quaternion, mix); - body.quaternion.set(quaternion[0], quaternion[1], quaternion[2], quaternion[3]); - body.velocity.set( - message1.velocity[0] * weight1 + message2.velocity[0] * weight2, - message1.velocity[1] * weight1 + message2.velocity[1] * weight2, - message1.velocity[2] * weight1 + message2.velocity[2] * weight2 - ); - body.angularVelocity.set( - message1.angularVelocity[0] * weight1 + message2.angularVelocity[0] * weight2, - message1.angularVelocity[1] * weight1 + message2.angularVelocity[1] * weight2, - message1.angularVelocity[2] * weight1 + message2.angularVelocity[2] * weight2 - ); - - body.linearDamping = message2.linearDamping; - body.angularDamping = message2.angularDamping; - body.fixedRotation = message2.fixedRotation; - body.allowSleep = message2.allowSleep; - body.sleepSpeedLimit = message2.sleepSpeedLimit; - body.sleepTimeLimit = message2.sleepTimeLimit; - - if (body.mass !== message2.mass) { - body.mass = message2.mass; - body.updateMassProperties(); - } - - return body; -}; - -module.exports.deserializeBody = function (message) { - var body = new CANNON.Body({ - mass: message.mass, - - position: deserializeVec3(message.position), - quaternion: deserializeQuaternion(message.quaternion), - velocity: deserializeVec3(message.velocity), - angularVelocity: deserializeVec3(message.angularVelocity), - - linearDamping: message.linearDamping, - angularDamping: message.angularDamping, - fixedRotation: message.fixedRotation, - allowSleep: message.allowSleep, - sleepSpeedLimit: message.sleepSpeedLimit, - sleepTimeLimit: message.sleepTimeLimit - }); - - for (var shapeMsg, i = 0; (shapeMsg = message.shapes[i]); i++) { - body.addShape( - deserializeShape(shapeMsg), - deserializeVec3(message.shapeOffsets[i]), - deserializeQuaternion(message.shapeOrientations[i]) - ); - } - - body[ID] = message.id; - - return body; -}; - -/****************************************************************************** - * Shapes - */ - -module.exports.serializeShape = serializeShape; -function serializeShape (shape) { - var shapeMsg = {type: shape.type}; - if (shape.type === CANNON.Shape.types.BOX) { - shapeMsg.halfExtents = serializeVec3(shape.halfExtents); - - } else if (shape.type === CANNON.Shape.types.SPHERE) { - shapeMsg.radius = shape.radius; - - // Patch schteppe/cannon.js#329. - } else if (shape._type === CANNON.Shape.types.CYLINDER) { - shapeMsg.type = CANNON.Shape.types.CYLINDER; - shapeMsg.radiusTop = shape.radiusTop; - shapeMsg.radiusBottom = shape.radiusBottom; - shapeMsg.height = shape.height; - shapeMsg.numSegments = shape.numSegments; - - } else { - // TODO(donmccurdy): Support for other shape types. - throw new Error('Unimplemented shape type: %s', shape.type); - } - return shapeMsg; -} - -module.exports.deserializeShape = deserializeShape; -function deserializeShape (message) { - var shape; - - if (message.type === CANNON.Shape.types.BOX) { - shape = new CANNON.Box(deserializeVec3(message.halfExtents)); - - } else if (message.type === CANNON.Shape.types.SPHERE) { - shape = new CANNON.Sphere(message.radius); - - // Patch schteppe/cannon.js#329. - } else if (message.type === CANNON.Shape.types.CYLINDER) { - shape = new CANNON.Cylinder(message.radiusTop, message.radiusBottom, message.height, message.numSegments); - shape._type = CANNON.Shape.types.CYLINDER; - - } else { - // TODO(donmccurdy): Support for other shape types. - throw new Error('Unimplemented shape type: %s', message.type); - } - - return shape; -} - -/****************************************************************************** - * Constraints - */ - -module.exports.serializeConstraint = function (constraint) { - - var message = { - id: constraint[ID], - type: constraint.type, - maxForce: constraint.maxForce, - bodyA: constraint.bodyA[ID], - bodyB: constraint.bodyB[ID] - }; - - switch (constraint.type) { - case 'LockConstraint': - break; - case 'DistanceConstraint': - message.distance = constraint.distance; - break; - case 'HingeConstraint': - case 'ConeTwistConstraint': - message.axisA = serializeVec3(constraint.axisA); - message.axisB = serializeVec3(constraint.axisB); - message.pivotA = serializeVec3(constraint.pivotA); - message.pivotB = serializeVec3(constraint.pivotB); - break; - case 'PointToPointConstraint': - message.pivotA = serializeVec3(constraint.pivotA); - message.pivotB = serializeVec3(constraint.pivotB); - break; - default: - throw new Error('' - + 'Unexpected constraint type: ' + constraint.type + '. ' - + 'You may need to manually set `constraint.type = "FooConstraint";`.' - ); - } - - return message; -}; - -module.exports.deserializeConstraint = function (message, bodies) { - var TypedConstraint = CANNON[message.type]; - var bodyA = bodies[message.bodyA]; - var bodyB = bodies[message.bodyB]; - - var constraint; - - switch (message.type) { - case 'LockConstraint': - constraint = new CANNON.LockConstraint(bodyA, bodyB, message); - break; - - case 'DistanceConstraint': - constraint = new CANNON.DistanceConstraint( - bodyA, - bodyB, - message.distance, - message.maxForce - ); - break; - - case 'HingeConstraint': - case 'ConeTwistConstraint': - constraint = new TypedConstraint(bodyA, bodyB, { - pivotA: deserializeVec3(message.pivotA), - pivotB: deserializeVec3(message.pivotB), - axisA: deserializeVec3(message.axisA), - axisB: deserializeVec3(message.axisB), - maxForce: message.maxForce - }); - break; - - case 'PointToPointConstraint': - constraint = new CANNON.PointToPointConstraint( - bodyA, - deserializeVec3(message.pivotA), - bodyB, - deserializeVec3(message.pivotB), - message.maxForce - ); - break; - - default: - throw new Error('Unexpected constraint type: ' + message.type); - } - - constraint[ID] = message.id; - return constraint; -}; - -/****************************************************************************** - * Contacts - */ - -module.exports.serializeContact = function (contact) { - return { - bi: contact.bi[ID], - bj: contact.bj[ID], - ni: serializeVec3(contact.ni), - ri: serializeVec3(contact.ri), - rj: serializeVec3(contact.rj) - }; -}; - -module.exports.deserializeContact = function (message, bodies) { - return { - bi: bodies[message.bi], - bj: bodies[message.bj], - ni: deserializeVec3(message.ni), - ri: deserializeVec3(message.ri), - rj: deserializeVec3(message.rj) - }; -}; - -/****************************************************************************** - * Math - */ - -module.exports.serializeVec3 = serializeVec3; -function serializeVec3 (vec3) { - return vec3.toArray(); -} - -module.exports.deserializeVec3 = deserializeVec3; -function deserializeVec3 (message) { - return new CANNON.Vec3(message[0], message[1], message[2]); -} - -module.exports.serializeQuaternion = serializeQuaternion; -function serializeQuaternion (quat) { - return quat.toArray(); -} - -module.exports.deserializeQuaternion = deserializeQuaternion; -function deserializeQuaternion (message) { - return new CANNON.Quaternion(message[0], message[1], message[2], message[3]); -} +var CANNON = require('cannon-es'); +var mathUtils = require('./math'); + +/****************************************************************************** + * IDs + */ + +var ID = '__id'; +module.exports.ID = ID; + +var nextID = {}; +module.exports.assignID = function (prefix, object) { + if (object[ID]) return; + nextID[prefix] = nextID[prefix] || 1; + object[ID] = prefix + '_' + nextID[prefix]++; +}; + +/****************************************************************************** + * Bodies + */ + +module.exports.serializeBody = function (body) { + var message = { + // Shapes. + shapes: body.shapes.map(serializeShape), + shapeOffsets: body.shapeOffsets.map(serializeVec3), + shapeOrientations: body.shapeOrientations.map(serializeQuaternion), + + // Vectors. + position: serializeVec3(body.position), + quaternion: body.quaternion.toArray(), + velocity: serializeVec3(body.velocity), + angularVelocity: serializeVec3(body.angularVelocity), + + // Properties. + id: body[ID], + mass: body.mass, + linearDamping: body.linearDamping, + angularDamping: body.angularDamping, + fixedRotation: body.fixedRotation, + allowSleep: body.allowSleep, + sleepSpeedLimit: body.sleepSpeedLimit, + sleepTimeLimit: body.sleepTimeLimit + }; + + return message; +}; + +module.exports.deserializeBodyUpdate = function (message, body) { + body.position.set(message.position[0], message.position[1], message.position[2]); + body.quaternion.set(message.quaternion[0], message.quaternion[1], message.quaternion[2], message.quaternion[3]); + body.velocity.set(message.velocity[0], message.velocity[1], message.velocity[2]); + body.angularVelocity.set(message.angularVelocity[0], message.angularVelocity[1], message.angularVelocity[2]); + + body.linearDamping = message.linearDamping; + body.angularDamping = message.angularDamping; + body.fixedRotation = message.fixedRotation; + body.allowSleep = message.allowSleep; + body.sleepSpeedLimit = message.sleepSpeedLimit; + body.sleepTimeLimit = message.sleepTimeLimit; + + if (body.mass !== message.mass) { + body.mass = message.mass; + body.updateMassProperties(); + } + + return body; +}; + +module.exports.deserializeInterpBodyUpdate = function (message1, message2, body, mix) { + var weight1 = 1 - mix; + var weight2 = mix; + + body.position.set( + message1.position[0] * weight1 + message2.position[0] * weight2, + message1.position[1] * weight1 + message2.position[1] * weight2, + message1.position[2] * weight1 + message2.position[2] * weight2 + ); + var quaternion = mathUtils.slerp(message1.quaternion, message2.quaternion, mix); + body.quaternion.set(quaternion[0], quaternion[1], quaternion[2], quaternion[3]); + body.velocity.set( + message1.velocity[0] * weight1 + message2.velocity[0] * weight2, + message1.velocity[1] * weight1 + message2.velocity[1] * weight2, + message1.velocity[2] * weight1 + message2.velocity[2] * weight2 + ); + body.angularVelocity.set( + message1.angularVelocity[0] * weight1 + message2.angularVelocity[0] * weight2, + message1.angularVelocity[1] * weight1 + message2.angularVelocity[1] * weight2, + message1.angularVelocity[2] * weight1 + message2.angularVelocity[2] * weight2 + ); + + body.linearDamping = message2.linearDamping; + body.angularDamping = message2.angularDamping; + body.fixedRotation = message2.fixedRotation; + body.allowSleep = message2.allowSleep; + body.sleepSpeedLimit = message2.sleepSpeedLimit; + body.sleepTimeLimit = message2.sleepTimeLimit; + + if (body.mass !== message2.mass) { + body.mass = message2.mass; + body.updateMassProperties(); + } + + return body; +}; + +module.exports.deserializeBody = function (message) { + var body = new CANNON.Body({ + mass: message.mass, + + position: deserializeVec3(message.position), + quaternion: deserializeQuaternion(message.quaternion), + velocity: deserializeVec3(message.velocity), + angularVelocity: deserializeVec3(message.angularVelocity), + + linearDamping: message.linearDamping, + angularDamping: message.angularDamping, + fixedRotation: message.fixedRotation, + allowSleep: message.allowSleep, + sleepSpeedLimit: message.sleepSpeedLimit, + sleepTimeLimit: message.sleepTimeLimit + }); + + for (var shapeMsg, i = 0; (shapeMsg = message.shapes[i]); i++) { + body.addShape( + deserializeShape(shapeMsg), + deserializeVec3(message.shapeOffsets[i]), + deserializeQuaternion(message.shapeOrientations[i]) + ); + } + + body[ID] = message.id; + + return body; +}; + +/****************************************************************************** + * Shapes + */ + +module.exports.serializeShape = serializeShape; +function serializeShape (shape) { + var shapeMsg = {type: shape.type}; + if (shape.type === CANNON.Shape.types.BOX) { + shapeMsg.halfExtents = serializeVec3(shape.halfExtents); + + } else if (shape.type === CANNON.Shape.types.SPHERE) { + shapeMsg.radius = shape.radius; + + // Patch schteppe/cannon.js#329. + } else if (shape._type === CANNON.Shape.types.CYLINDER) { + shapeMsg.type = CANNON.Shape.types.CYLINDER; + shapeMsg.radiusTop = shape.radiusTop; + shapeMsg.radiusBottom = shape.radiusBottom; + shapeMsg.height = shape.height; + shapeMsg.numSegments = shape.numSegments; + + } else { + // TODO(donmccurdy): Support for other shape types. + throw new Error('Unimplemented shape type: %s', shape.type); + } + return shapeMsg; +} + +module.exports.deserializeShape = deserializeShape; +function deserializeShape (message) { + var shape; + + if (message.type === CANNON.Shape.types.BOX) { + shape = new CANNON.Box(deserializeVec3(message.halfExtents)); + + } else if (message.type === CANNON.Shape.types.SPHERE) { + shape = new CANNON.Sphere(message.radius); + + // Patch schteppe/cannon.js#329. + } else if (message.type === CANNON.Shape.types.CYLINDER) { + shape = new CANNON.Cylinder(message.radiusTop, message.radiusBottom, message.height, message.numSegments); + shape._type = CANNON.Shape.types.CYLINDER; + + } else { + // TODO(donmccurdy): Support for other shape types. + throw new Error('Unimplemented shape type: %s', message.type); + } + + return shape; +} + +/****************************************************************************** + * Constraints + */ + +module.exports.serializeConstraint = function (constraint) { + + var message = { + id: constraint[ID], + type: constraint.type, + maxForce: constraint.maxForce, + bodyA: constraint.bodyA[ID], + bodyB: constraint.bodyB[ID] + }; + + switch (constraint.type) { + case 'LockConstraint': + break; + case 'DistanceConstraint': + message.distance = constraint.distance; + break; + case 'HingeConstraint': + case 'ConeTwistConstraint': + message.axisA = serializeVec3(constraint.axisA); + message.axisB = serializeVec3(constraint.axisB); + message.pivotA = serializeVec3(constraint.pivotA); + message.pivotB = serializeVec3(constraint.pivotB); + break; + case 'PointToPointConstraint': + message.pivotA = serializeVec3(constraint.pivotA); + message.pivotB = serializeVec3(constraint.pivotB); + break; + default: + throw new Error('' + + 'Unexpected constraint type: ' + constraint.type + '. ' + + 'You may need to manually set `constraint.type = "FooConstraint";`.' + ); + } + + return message; +}; + +module.exports.deserializeConstraint = function (message, bodies) { + var TypedConstraint = CANNON[message.type]; + var bodyA = bodies[message.bodyA]; + var bodyB = bodies[message.bodyB]; + + var constraint; + + switch (message.type) { + case 'LockConstraint': + constraint = new CANNON.LockConstraint(bodyA, bodyB, message); + break; + + case 'DistanceConstraint': + constraint = new CANNON.DistanceConstraint( + bodyA, + bodyB, + message.distance, + message.maxForce + ); + break; + + case 'HingeConstraint': + case 'ConeTwistConstraint': + constraint = new TypedConstraint(bodyA, bodyB, { + pivotA: deserializeVec3(message.pivotA), + pivotB: deserializeVec3(message.pivotB), + axisA: deserializeVec3(message.axisA), + axisB: deserializeVec3(message.axisB), + maxForce: message.maxForce + }); + break; + + case 'PointToPointConstraint': + constraint = new CANNON.PointToPointConstraint( + bodyA, + deserializeVec3(message.pivotA), + bodyB, + deserializeVec3(message.pivotB), + message.maxForce + ); + break; + + default: + throw new Error('Unexpected constraint type: ' + message.type); + } + + constraint[ID] = message.id; + return constraint; +}; + +/****************************************************************************** + * Contacts + */ + +module.exports.serializeContact = function (contact) { + return { + bi: contact.bi[ID], + bj: contact.bj[ID], + ni: serializeVec3(contact.ni), + ri: serializeVec3(contact.ri), + rj: serializeVec3(contact.rj) + }; +}; + +module.exports.deserializeContact = function (message, bodies) { + return { + bi: bodies[message.bi], + bj: bodies[message.bj], + ni: deserializeVec3(message.ni), + ri: deserializeVec3(message.ri), + rj: deserializeVec3(message.rj) + }; +}; + +/****************************************************************************** + * Math + */ + +module.exports.serializeVec3 = serializeVec3; +function serializeVec3 (vec3) { + return vec3.toArray(); +} + +module.exports.deserializeVec3 = deserializeVec3; +function deserializeVec3 (message) { + return new CANNON.Vec3(message[0], message[1], message[2]); +} + +module.exports.serializeQuaternion = serializeQuaternion; +function serializeQuaternion (quat) { + return quat.toArray(); +} + +module.exports.deserializeQuaternion = deserializeQuaternion; +function deserializeQuaternion (message) { + return new CANNON.Quaternion(message[0], message[1], message[2], message[3]); +} },{"./math":30,"cannon-es":5}]},{},[1]); diff --git a/dist/aframe-physics-system.min.js b/dist/aframe-physics-system.min.js index e4001cf..97e70d9 100644 --- a/dist/aframe-physics-system.min.js +++ b/dist/aframe-physics-system.min.js @@ -1 +1 @@ -!function(){return function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){return o(e[i][1][r]||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i{const counterValue=document.createElement("div");counterValue.classList.add("rs-counter-value"),counterValue.innerHTML="...",this.counter.appendChild(counterValue),this.counterValues[property]=counterValue}),this.updateData=this.updateData.bind(this),this.el.addEventListener(this.data.event,this.updateData),this.splitCache={}):console.warn(`Couldn't find stats group ${groupComponentName}`)},updateData(e){this.data.properties.forEach(property=>{const split=this.splitDot(property);let value=e.detail;for(i=0;i{this.outputDetail[property]={}}),this.statsReceived=this.statsReceived.bind(this),this.el.addEventListener(this.data.inEvent,this.statsReceived)},resetData(){this.counter=0,this.data.properties.forEach(property=>{this.statsData[property]=[]})},statsReceived(e){this.updateData(e.detail),this.counter++,this.counter===this.data.outputFrequency&&(this.outputData(),this.resetData())},updateData(detail){this.data.properties.forEach(property=>{let value=detail;value=value[property],this.statsData[property].push(value)})},outputData(){this.data.properties.forEach(property=>{this.data.outputs.forEach(output=>{this.outputDetail[property][output]=this.computeOutput(output,this.statsData[property])})}),this.data.outEvent&&this.el.emit(this.data.outEvent,this.outputDetail),this.data.outputToConsole&&console.log(this.data.outputToConsole,this.outputDetail)},computeOutput(outputInstruction,data){const outputInstructions=outputInstruction.split("__");let output;switch(outputInstructions[0]){case"mean":output=data.reduce((a,b)=>a+b,0)/data.length;break;case"max":output=Math.max(...data);break;case"min":output=Math.min(...data);break;case"percentile":const sorted=data.sort((a,b)=>a-b),proportion=+outputInstructions[1].replace("_",".")/100,position=(data.length-1)*proportion,base=Math.floor(position),delta=position-base;output=void 0!==sorted[base+1]?sorted[base]+delta*(sorted[base+1]-sorted[base]):sorted[base]}return output.toFixed(2)}})},{}],4:[function(require,module,exports){THREE.AmmoDebugConstants={NoDebug:0,DrawWireframe:1,DrawAabb:2,DrawFeaturesText:4,DrawContactPoints:8,NoDeactivation:16,NoHelpText:32,DrawText:64,ProfileTimings:128,EnableSatComparison:256,DisableBulletLCP:512,EnableCCD:1024,DrawConstraints:2048,DrawConstraintLimits:4096,FastWireframe:8192,DrawNormals:16384,DrawOnTop:32768,MAX_DEBUG_DRAW_MODE:4294967295},THREE.AmmoDebugDrawer=function(scene,world,options){this.scene=scene,this.world=world,options=options||{},this.debugDrawMode=options.debugDrawMode||THREE.AmmoDebugConstants.DrawWireframe;var drawOnTop=this.debugDrawMode&THREE.AmmoDebugConstants.DrawOnTop||!1,maxBufferSize=options.maxBufferSize||1e6;this.geometry=new THREE.BufferGeometry;var vertices=new Float32Array(3*maxBufferSize),colors=new Float32Array(3*maxBufferSize);this.geometry.setAttribute("position",new THREE.BufferAttribute(vertices,3).setUsage(THREE.DynamicDrawUsage)),this.geometry.setAttribute("color",new THREE.BufferAttribute(colors,3).setUsage(THREE.DynamicDrawUsage)),this.index=0;var material=new THREE.LineBasicMaterial({vertexColors:!0,depthTest:!drawOnTop});this.mesh=new THREE.LineSegments(this.geometry,material),drawOnTop&&(this.mesh.renderOrder=999),this.mesh.frustumCulled=!1,this.enabled=!1,this.debugDrawer=new Ammo.DebugDrawer,this.debugDrawer.drawLine=this.drawLine.bind(this),this.debugDrawer.drawContactPoint=this.drawContactPoint.bind(this),this.debugDrawer.reportErrorWarning=this.reportErrorWarning.bind(this),this.debugDrawer.draw3dText=this.draw3dText.bind(this),this.debugDrawer.setDebugMode=this.setDebugMode.bind(this),this.debugDrawer.getDebugMode=this.getDebugMode.bind(this),this.debugDrawer.enable=this.enable.bind(this),this.debugDrawer.disable=this.disable.bind(this),this.debugDrawer.update=this.update.bind(this),this.world.setDebugDrawer(this.debugDrawer)},THREE.AmmoDebugDrawer.prototype=function(){return this.debugDrawer},THREE.AmmoDebugDrawer.prototype.enable=function(){this.enabled=!0,this.scene.add(this.mesh)},THREE.AmmoDebugDrawer.prototype.disable=function(){this.enabled=!1,this.scene.remove(this.mesh)},THREE.AmmoDebugDrawer.prototype.update=function(){this.enabled&&(0!=this.index&&(this.geometry.attributes.position.needsUpdate=!0,this.geometry.attributes.color.needsUpdate=!0),this.index=0,this.world.debugDrawWorld(),this.geometry.setDrawRange(0,this.index))},THREE.AmmoDebugDrawer.prototype.drawLine=function(from,to,color){const heap=Ammo.HEAPF32,r=heap[(color+0)/4],g=heap[(color+4)/4],b=heap[(color+8)/4],fromX=heap[(from+0)/4],fromY=heap[(from+4)/4],fromZ=heap[(from+8)/4];this.geometry.attributes.position.setXYZ(this.index,fromX,fromY,fromZ),this.geometry.attributes.color.setXYZ(this.index++,r,g,b);const toX=heap[(to+0)/4],toY=heap[(to+4)/4],toZ=heap[(to+8)/4];this.geometry.attributes.position.setXYZ(this.index,toX,toY,toZ),this.geometry.attributes.color.setXYZ(this.index++,r,g,b)},THREE.AmmoDebugDrawer.prototype.drawContactPoint=function(pointOnB,normalOnB,distance,lifeTime,color){const heap=Ammo.HEAPF32,r=heap[(color+0)/4],g=heap[(color+4)/4],b=heap[(color+8)/4],x=heap[(pointOnB+0)/4],y=heap[(pointOnB+4)/4],z=heap[(pointOnB+8)/4];this.geometry.attributes.position.setXYZ(this.index,x,y,z),this.geometry.attributes.color.setXYZ(this.index++,r,g,b);const dx=heap[(normalOnB+0)/4]*distance,dy=heap[(normalOnB+4)/4]*distance,dz=heap[(normalOnB+8)/4]*distance;this.geometry.attributes.position.setXYZ(this.index,x+dx,y+dy,z+dz),this.geometry.attributes.color.setXYZ(this.index++,r,g,b)},THREE.AmmoDebugDrawer.prototype.reportErrorWarning=function(warningString){Ammo.hasOwnProperty("Pointer_stringify")?console.warn(Ammo.Pointer_stringify(warningString)):this.warnedOnce||(this.warnedOnce=!0,console.warn("Cannot print warningString, please rebuild Ammo.js using 'debug' flag"))},THREE.AmmoDebugDrawer.prototype.draw3dText=function(location,textString){console.warn("TODO: draw3dText")},THREE.AmmoDebugDrawer.prototype.setDebugMode=function(debugMode){this.debugDrawMode=debugMode},THREE.AmmoDebugDrawer.prototype.getDebugMode=function(){return this.debugDrawMode}},{}],5:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.World=exports.Vec3Pool=exports.Vec3=exports.Trimesh=exports.Transform=exports.Spring=exports.SplitSolver=exports.Sphere=exports.Solver=exports.Shape=exports.SPHSystem=exports.SHAPE_TYPES=exports.SAPBroadphase=exports.RotationalMotorEquation=exports.RotationalEquation=exports.RigidVehicle=exports.RaycastVehicle=exports.RaycastResult=exports.Ray=exports.RAY_MODES=exports.Quaternion=exports.Pool=exports.PointToPointConstraint=exports.Plane=exports.Particle=exports.ObjectCollisionMatrix=exports.Narrowphase=exports.NaiveBroadphase=exports.Material=exports.Mat3=exports.LockConstraint=exports.JacobianElement=exports.HingeConstraint=exports.Heightfield=exports.GridBroadphase=exports.GSSolver=exports.FrictionEquation=exports.EventTarget=exports.Equation=exports.DistanceConstraint=exports.Cylinder=exports.ConvexPolyhedron=exports.ContactMaterial=exports.ContactEquation=exports.Constraint=exports.ConeTwistConstraint=exports.COLLISION_TYPES=exports.Broadphase=exports.Box=exports.Body=exports.BODY_TYPES=exports.BODY_SLEEP_STATES=exports.ArrayCollisionMatrix=exports.AABB=void 0;exports.ObjectCollisionMatrix=class{constructor(){this.matrix={}}get(bi,bj){let{id:i}=bi,{id:j}=bj;if(j>i){const temp=j;j=i,i=temp}return i+"-"+j in this.matrix}set(bi,bj,value){let{id:i}=bi,{id:j}=bj;if(j>i){const temp=j;j=i,i=temp}value?this.matrix[i+"-"+j]=!0:delete this.matrix[i+"-"+j]}reset(){this.matrix={}}setNumObjects(n){}};class Mat3{constructor(elements=[0,0,0,0,0,0,0,0,0]){this.elements=elements}identity(){const e=this.elements;e[0]=1,e[1]=0,e[2]=0,e[3]=0,e[4]=1,e[5]=0,e[6]=0,e[7]=0,e[8]=1}setZero(){const e=this.elements;e[0]=0,e[1]=0,e[2]=0,e[3]=0,e[4]=0,e[5]=0,e[6]=0,e[7]=0,e[8]=0}setTrace(vector){const e=this.elements;e[0]=vector.x,e[4]=vector.y,e[8]=vector.z}getTrace(target=new Vec3){const e=this.elements;target.x=e[0],target.y=e[4],target.z=e[8]}vmult(v,target=new Vec3){const e=this.elements,x=v.x,y=v.y,z=v.z;return target.x=e[0]*x+e[1]*y+e[2]*z,target.y=e[3]*x+e[4]*y+e[5]*z,target.z=e[6]*x+e[7]*y+e[8]*z,target}smult(s){for(let i=0;i0){const invN=1/n;this.x*=invN,this.y*=invN,this.z*=invN}else this.x=0,this.y=0,this.z=0;return n}unit(target=new Vec3){const x=this.x,y=this.y,z=this.z;let ninv=Math.sqrt(x*x+y*y+z*z);return ninv>0?(ninv=1/ninv,target.x=x*ninv,target.y=y*ninv,target.z=z*ninv):(target.x=1,target.y=0,target.z=0),target}length(){const x=this.x,y=this.y,z=this.z;return Math.sqrt(x*x+y*y+z*z)}lengthSquared(){return this.dot(this)}distanceTo(p){const x=this.x,y=this.y,z=this.z,px=p.x,py=p.y,pz=p.z;return Math.sqrt((px-x)*(px-x)+(py-y)*(py-y)+(pz-z)*(pz-z))}distanceSquared(p){const x=this.x,y=this.y,z=this.z,px=p.x,py=p.y,pz=p.z;return(px-x)*(px-x)+(py-y)*(py-y)+(pz-z)*(pz-z)}scale(scalar,target=new Vec3){const x=this.x,y=this.y,z=this.z;return target.x=scalar*x,target.y=scalar*y,target.z=scalar*z,target}vmul(vector,target=new Vec3){return target.x=vector.x*this.x,target.y=vector.y*this.y,target.z=vector.z*this.z,target}addScaledVector(scalar,vector,target=new Vec3){return target.x=this.x+scalar*vector.x,target.y=this.y+scalar*vector.y,target.z=this.z+scalar*vector.z,target}dot(vector){return this.x*vector.x+this.y*vector.y+this.z*vector.z}isZero(){return 0===this.x&&0===this.y&&0===this.z}negate(target=new Vec3){return target.x=-this.x,target.y=-this.y,target.z=-this.z,target}tangents(t1,t2){const norm=this.length();if(norm>0){const n=Vec3_tangents_n,inorm=1/norm;n.set(this.x*inorm,this.y*inorm,this.z*inorm);const randVec=Vec3_tangents_randVec;Math.abs(n.x)<.9?(randVec.set(1,0,0),n.cross(randVec,t1)):(randVec.set(0,1,0),n.cross(randVec,t1)),n.cross(t1,t2)}else t1.set(1,0,0),t2.set(0,1,0)}toString(){return this.x+","+this.y+","+this.z}toArray(){return[this.x,this.y,this.z]}copy(vector){return this.x=vector.x,this.y=vector.y,this.z=vector.z,this}lerp(vector,t,target){const x=this.x,y=this.y,z=this.z;target.x=x+(vector.x-x)*t,target.y=y+(vector.y-y)*t,target.z=z+(vector.z-z)*t}almostEquals(vector,precision=1e-6){return!(Math.abs(this.x-vector.x)>precision||Math.abs(this.y-vector.y)>precision||Math.abs(this.z-vector.z)>precision)}almostZero(precision=1e-6){return!(Math.abs(this.x)>precision||Math.abs(this.y)>precision||Math.abs(this.z)>precision)}isAntiparallelTo(vector,precision){return this.negate(antip_neg),antip_neg.almostEquals(vector,precision)}clone(){return new Vec3(this.x,this.y,this.z)}}exports.Vec3=Vec3,Vec3.ZERO=new Vec3(0,0,0),Vec3.UNIT_X=new Vec3(1,0,0),Vec3.UNIT_Y=new Vec3(0,1,0),Vec3.UNIT_Z=new Vec3(0,0,1);const Vec3_tangents_n=new Vec3,Vec3_tangents_randVec=new Vec3,antip_neg=new Vec3;class AABB{constructor(options={}){this.lowerBound=new Vec3,this.upperBound=new Vec3,options.lowerBound&&this.lowerBound.copy(options.lowerBound),options.upperBound&&this.upperBound.copy(options.upperBound)}setFromPoints(points,position,quaternion,skinSize){const l=this.lowerBound,u=this.upperBound,q=quaternion;l.copy(points[0]),q&&q.vmult(l,l),u.copy(l);for(let i=1;iu.x&&(u.x=p.x),p.xu.y&&(u.y=p.y),p.yu.z&&(u.z=p.z),p.z=u2.x&&l1.y<=l2.y&&u1.y>=u2.y&&l1.z<=l2.z&&u1.z>=u2.z}getCorners(a,b,c,d,e,f,g,h){const l=this.lowerBound,u=this.upperBound;a.copy(l),b.set(u.x,l.y,l.z),c.set(u.x,u.y,l.z),d.set(l.x,u.y,u.z),e.set(u.x,l.y,u.z),f.set(l.x,u.y,l.z),g.set(l.x,l.y,u.z),h.copy(u)}toLocalFrame(frame,target){const corners=transformIntoFrame_corners,a=corners[0],b=corners[1],c=corners[2],d=corners[3],e=corners[4],f=corners[5],g=corners[6],h=corners[7];this.getCorners(a,b,c,d,e,f,g,h);for(let i=0;8!==i;i++){const corner=corners[i];frame.pointToLocal(corner,corner)}return target.setFromPoints(corners)}toWorldFrame(frame,target){const corners=transformIntoFrame_corners,a=corners[0],b=corners[1],c=corners[2],d=corners[3],e=corners[4],f=corners[5],g=corners[6],h=corners[7];this.getCorners(a,b,c,d,e,f,g,h);for(let i=0;8!==i;i++){const corner=corners[i];frame.pointToWorld(corner,corner)}return target.setFromPoints(corners)}overlapsRay(ray){const{direction:direction,from:from}=ray,dirFracX=1/direction.x,dirFracY=1/direction.y,dirFracZ=1/direction.z,t1=(this.lowerBound.x-from.x)*dirFracX,t2=(this.upperBound.x-from.x)*dirFracX,t3=(this.lowerBound.y-from.y)*dirFracY,t4=(this.upperBound.y-from.y)*dirFracY,t5=(this.lowerBound.z-from.z)*dirFracZ,t6=(this.upperBound.z-from.z)*dirFracZ,tmin=Math.max(Math.max(Math.min(t1,t2),Math.min(t3,t4)),Math.min(t5,t6)),tmax=Math.min(Math.min(Math.max(t1,t2),Math.max(t3,t4)),Math.max(t5,t6));return!(tmax<0)&&!(tmin>tmax)}}exports.AABB=AABB;const tmp=new Vec3,transformIntoFrame_corners=[new Vec3,new Vec3,new Vec3,new Vec3,new Vec3,new Vec3,new Vec3,new Vec3];class ArrayCollisionMatrix{constructor(){this.matrix=[]}get(bi,bj){let{index:i}=bi,{index:j}=bj;if(j>i){const temp=j;j=i,i=temp}return this.matrix[(i*(i+1)>>1)+j-1]}set(bi,bj,value){let{index:i}=bi,{index:j}=bj;if(j>i){const temp=j;j=i,i=temp}this.matrix[(i*(i+1)>>1)+j-1]=value?1:0}reset(){for(let i=0,l=this.matrix.length;i!==l;i++)this.matrix[i]=0}setNumObjects(n){this.matrix.length=n*(n-1)>>1}}exports.ArrayCollisionMatrix=ArrayCollisionMatrix;class EventTarget{constructor(){}addEventListener(type,listener){void 0===this._listeners&&(this._listeners={});const listeners=this._listeners;return void 0===listeners[type]&&(listeners[type]=[]),listeners[type].includes(listener)||listeners[type].push(listener),this}hasEventListener(type,listener){if(void 0===this._listeners)return!1;const listeners=this._listeners;return!(void 0===listeners[type]||!listeners[type].includes(listener))}hasAnyEventListener(type){if(void 0===this._listeners)return!1;return void 0!==this._listeners[type]}removeEventListener(type,listener){if(void 0===this._listeners)return this;const listeners=this._listeners;if(void 0===listeners[type])return this;const index=listeners[type].indexOf(listener);return-1!==index&&listeners[type].splice(index,1),this}dispatchEvent(event){if(void 0===this._listeners)return this;const listenerArray=this._listeners[event.type];if(void 0!==listenerArray){event.target=this;for(let i=0,l=listenerArray.length;i.499&&(heading=2*Math.atan2(x,w),attitude=Math.PI/2,bank=0),test<-.499&&(heading=-2*Math.atan2(x,w),attitude=-Math.PI/2,bank=0),void 0===heading){const sqx=x*x,sqy=y*y,sqz=z*z;heading=Math.atan2(2*y*w-2*x*z,1-2*sqy-2*sqz),attitude=Math.asin(2*test),bank=Math.atan2(2*x*w-2*y*z,1-2*sqx-2*sqz)}break;default:throw new Error("Euler order "+order+" not supported yet.")}target.y=heading,target.z=attitude,target.x=bank}setFromEuler(x,y,z,order="XYZ"){const c1=Math.cos(x/2),c2=Math.cos(y/2),c3=Math.cos(z/2),s1=Math.sin(x/2),s2=Math.sin(y/2),s3=Math.sin(z/2);return"XYZ"===order?(this.x=s1*c2*c3+c1*s2*s3,this.y=c1*s2*c3-s1*c2*s3,this.z=c1*c2*s3+s1*s2*c3,this.w=c1*c2*c3-s1*s2*s3):"YXZ"===order?(this.x=s1*c2*c3+c1*s2*s3,this.y=c1*s2*c3-s1*c2*s3,this.z=c1*c2*s3-s1*s2*c3,this.w=c1*c2*c3+s1*s2*s3):"ZXY"===order?(this.x=s1*c2*c3-c1*s2*s3,this.y=c1*s2*c3+s1*c2*s3,this.z=c1*c2*s3+s1*s2*c3,this.w=c1*c2*c3-s1*s2*s3):"ZYX"===order?(this.x=s1*c2*c3-c1*s2*s3,this.y=c1*s2*c3+s1*c2*s3,this.z=c1*c2*s3-s1*s2*c3,this.w=c1*c2*c3+s1*s2*s3):"YZX"===order?(this.x=s1*c2*c3+c1*s2*s3,this.y=c1*s2*c3+s1*c2*s3,this.z=c1*c2*s3-s1*s2*c3,this.w=c1*c2*c3-s1*s2*s3):"XZY"===order&&(this.x=s1*c2*c3-c1*s2*s3,this.y=c1*s2*c3-s1*c2*s3,this.z=c1*c2*s3+s1*s2*c3,this.w=c1*c2*c3+s1*s2*s3),this}clone(){return new Quaternion(this.x,this.y,this.z,this.w)}slerp(toQuat,t,target=new Quaternion){const ax=this.x,ay=this.y,az=this.z,aw=this.w;let omega,cosom,sinom,scale0,scale1,bx=toQuat.x,by=toQuat.y,bz=toQuat.z,bw=toQuat.w;return(cosom=ax*bx+ay*by+az*bz+aw*bw)<0&&(cosom=-cosom,bx=-bx,by=-by,bz=-bz,bw=-bw),1-cosom>1e-6?(omega=Math.acos(cosom),sinom=Math.sin(omega),scale0=Math.sin((1-t)*omega)/sinom,scale1=Math.sin(t*omega)/sinom):(scale0=1-t,scale1=t),target.x=scale0*ax+scale1*bx,target.y=scale0*ay+scale1*by,target.z=scale0*az+scale1*bz,target.w=scale0*aw+scale1*bw,target}integrate(angularVelocity,dt,angularFactor,target=new Quaternion){const ax=angularVelocity.x*angularFactor.x,ay=angularVelocity.y*angularFactor.y,az=angularVelocity.z*angularFactor.z,bx=this.x,by=this.y,bz=this.z,bw=this.w,half_dt=.5*dt;return target.x+=half_dt*(ax*bw+ay*bz-az*by),target.y+=half_dt*(ay*bw+az*bx-ax*bz),target.z+=half_dt*(az*bw+ax*by-ay*bx),target.w+=half_dt*(-ax*bx-ay*by-az*bz),target}}exports.Quaternion=Quaternion;const sfv_t1=new Vec3,sfv_t2=new Vec3,SHAPE_TYPES=exports.SHAPE_TYPES={SPHERE:1,PLANE:2,BOX:4,COMPOUND:8,CONVEXPOLYHEDRON:16,HEIGHTFIELD:32,PARTICLE:64,CYLINDER:128,TRIMESH:256};class Shape{constructor(options={}){this.id=Shape.idCounter++,this.type=options.type||0,this.boundingSphereRadius=0,this.collisionResponse=!options.collisionResponse||options.collisionResponse,this.collisionFilterGroup=void 0!==options.collisionFilterGroup?options.collisionFilterGroup:1,this.collisionFilterMask=void 0!==options.collisionFilterMask?options.collisionFilterMask:-1,this.material=options.material?options.material:null,this.body=null}updateBoundingSphereRadius(){throw"computeBoundingSphereRadius() not implemented for shape type "+this.type}volume(){throw"volume() not implemented for shape type "+this.type}calculateLocalInertia(mass,target){throw"calculateLocalInertia() not implemented for shape type "+this.type}calculateWorldAABB(pos,quat,min,max){throw"calculateWorldAABB() not implemented for shape type "+this.type}}exports.Shape=Shape,Shape.idCounter=0,Shape.types=SHAPE_TYPES;class Transform{constructor(options={}){this.position=new Vec3,this.quaternion=new Quaternion,options.position&&this.position.copy(options.position),options.quaternion&&this.quaternion.copy(options.quaternion)}pointToLocal(worldPoint,result){return Transform.pointToLocalFrame(this.position,this.quaternion,worldPoint,result)}pointToWorld(localPoint,result){return Transform.pointToWorldFrame(this.position,this.quaternion,localPoint,result)}vectorToWorldFrame(localVector,result=new Vec3){return this.quaternion.vmult(localVector,result),result}static pointToLocalFrame(position,quaternion,worldPoint,result=new Vec3){return worldPoint.vsub(position,result),quaternion.conjugate(tmpQuat),tmpQuat.vmult(result,result),result}static pointToWorldFrame(position,quaternion,localPoint,result=new Vec3){return quaternion.vmult(localPoint,result),result.vadd(position,result),result}static vectorToWorldFrame(quaternion,localVector,result=new Vec3){return quaternion.vmult(localVector,result),result}static vectorToLocalFrame(position,quaternion,worldVector,result=new Vec3){return quaternion.w*=-1,quaternion.vmult(worldVector,result),quaternion.w*=-1,result}}exports.Transform=Transform;const tmpQuat=new Quaternion;class ConvexPolyhedron extends Shape{constructor(props={}){const{vertices:vertices=[],faces:faces=[],normals:normals=[],axes:axes,boundingSphereRadius:boundingSphereRadius}=props;super({type:Shape.types.CONVEXPOLYHEDRON}),this.vertices=vertices,this.faces=faces,this.faceNormals=normals,0===this.faceNormals.length&&this.computeNormals(),boundingSphereRadius?this.boundingSphereRadius=boundingSphereRadius:this.updateBoundingSphereRadius(),this.worldVertices=[],this.worldVerticesNeedsUpdate=!0,this.worldFaceNormals=[],this.worldFaceNormalsNeedsUpdate=!0,this.uniqueAxes=axes?axes.slice():null,this.uniqueEdges=[],this.computeEdges()}computeEdges(){const faces=this.faces,vertices=this.vertices,edges=this.uniqueEdges;edges.length=0;const edge=new Vec3;for(let i=0;i!==faces.length;i++){const face=faces[i],numVertices=face.length;for(let j=0;j!==numVertices;j++){const k=(j+1)%numVertices;vertices[face[j]].vsub(vertices[face[k]],edge),edge.normalize();let found=!1;for(let p=0;p!==edges.length;p++)if(edges[p].almostEquals(edge)||edges[p].almostEquals(edge)){found=!0;break}found||edges.push(edge.clone())}}}computeNormals(){this.faceNormals.length=this.faces.length;for(let i=0;idmax&&(dmax=d,closestFaceB=face)}const worldVertsB1=[];for(let i=0;i=0&&this.clipFaceAgainstHull(separatingNormal,posA,quatA,worldVertsB1,minDist,maxDist,result)}findSeparatingAxis(hullB,posA,quatA,posB,quatB,target,faceListA,faceListB){const faceANormalWS3=new Vec3,Worldnormal1=new Vec3,deltaC=new Vec3,worldEdge0=new Vec3,worldEdge1=new Vec3,Cross=new Vec3;let dmin=Number.MAX_VALUE;const hullA=this;if(hullA.uniqueAxes)for(let i=0;i!==hullA.uniqueAxes.length;i++){quatA.vmult(hullA.uniqueAxes[i],faceANormalWS3);const d=hullA.testSepAxis(faceANormalWS3,hullB,posA,quatA,posB,quatB);if(!1===d)return!1;d0&&target.negate(target),!0}testSepAxis(axis,hullB,posA,quatA,posB,quatB){ConvexPolyhedron.project(this,axis,posA,quatA,maxminA),ConvexPolyhedron.project(hullB,axis,posB,quatB,maxminB);const maxA=maxminA[0],minA=maxminA[1],maxB=maxminB[0],minB=maxminB[1];if(maxA0?1/mass:0,this.material=options.material||null,this.linearDamping="number"==typeof options.linearDamping?options.linearDamping:.01,this.type=mass<=0?Body.STATIC:Body.DYNAMIC,typeof options.type==typeof Body.STATIC&&(this.type=options.type),this.allowSleep=void 0===options.allowSleep||options.allowSleep,this.sleepState=0,this.sleepSpeedLimit=void 0!==options.sleepSpeedLimit?options.sleepSpeedLimit:.1,this.sleepTimeLimit=void 0!==options.sleepTimeLimit?options.sleepTimeLimit:1,this.timeLastSleepy=0,this.wakeUpAfterNarrowphase=!1,this.torque=new Vec3,this.quaternion=new Quaternion,this.initQuaternion=new Quaternion,this.previousQuaternion=new Quaternion,this.interpolatedQuaternion=new Quaternion,options.quaternion&&(this.quaternion.copy(options.quaternion),this.initQuaternion.copy(options.quaternion),this.previousQuaternion.copy(options.quaternion),this.interpolatedQuaternion.copy(options.quaternion)),this.angularVelocity=new Vec3,options.angularVelocity&&this.angularVelocity.copy(options.angularVelocity),this.initAngularVelocity=new Vec3,this.shapes=[],this.shapeOffsets=[],this.shapeOrientations=[],this.inertia=new Vec3,this.invInertia=new Vec3,this.invInertiaWorld=new Mat3,this.invMassSolve=0,this.invInertiaSolve=new Vec3,this.invInertiaWorldSolve=new Mat3,this.fixedRotation=void 0!==options.fixedRotation&&options.fixedRotation,this.angularDamping=void 0!==options.angularDamping?options.angularDamping:.01,this.linearFactor=new Vec3(1,1,1),options.linearFactor&&this.linearFactor.copy(options.linearFactor),this.angularFactor=new Vec3(1,1,1),options.angularFactor&&this.angularFactor.copy(options.angularFactor),this.aabb=new AABB,this.aabbNeedsUpdate=!0,this.boundingRadius=0,this.wlambda=new Vec3,options.shape&&this.addShape(options.shape),this.updateMassProperties()}wakeUp(){const prevState=this.sleepState;this.sleepState=0,this.wakeUpAfterNarrowphase=!1,prevState===Body.SLEEPING&&this.dispatchEvent(Body.wakeupEvent)}sleep(){this.sleepState=Body.SLEEPING,this.velocity.set(0,0,0),this.angularVelocity.set(0,0,0),this.wakeUpAfterNarrowphase=!1}sleepTick(time){if(this.allowSleep){const sleepState=this.sleepState,speedSquared=this.velocity.lengthSquared()+this.angularVelocity.lengthSquared(),speedLimitSquared=this.sleepSpeedLimit**2;sleepState===Body.AWAKE&&speedSquaredspeedLimitSquared?this.wakeUp():sleepState===Body.SLEEPY&&time-this.timeLastSleepy>this.sleepTimeLimit&&(this.sleep(),this.dispatchEvent(Body.sleepEvent))}}updateSolveMassProperties(){this.sleepState===Body.SLEEPING||this.type===Body.KINEMATIC?(this.invMassSolve=0,this.invInertiaSolve.setZero(),this.invInertiaWorldSolve.setZero()):(this.invMassSolve=this.invMass,this.invInertiaSolve.copy(this.invInertia),this.invInertiaWorldSolve.copy(this.invInertiaWorld))}pointToLocalFrame(worldPoint,result=new Vec3){return worldPoint.vsub(this.position,result),this.quaternion.conjugate().vmult(result,result),result}vectorToLocalFrame(worldVector,result=new Vec3){return this.quaternion.conjugate().vmult(worldVector,result),result}pointToWorldFrame(localPoint,result=new Vec3){return this.quaternion.vmult(localPoint,result),result.vadd(this.position,result),result}vectorToWorldFrame(localVector,result=new Vec3){return this.quaternion.vmult(localVector,result),result}addShape(shape,_offset,_orientation){const offset=new Vec3,orientation=new Quaternion;return _offset&&offset.copy(_offset),_orientation&&orientation.copy(_orientation),this.shapes.push(shape),this.shapeOffsets.push(offset),this.shapeOrientations.push(orientation),this.updateMassProperties(),this.updateBoundingRadius(),this.aabbNeedsUpdate=!0,shape.body=this,this}updateBoundingRadius(){const shapes=this.shapes,shapeOffsets=this.shapeOffsets,N=shapes.length;let radius=0;for(let i=0;i!==N;i++){const shape=shapes[i];shape.updateBoundingSphereRadius();const offset=shapeOffsets[i].length(),r=shape.boundingSphereRadius;offset+r>radius&&(radius=offset+r)}this.boundingRadius=radius}computeAABB(){const shapes=this.shapes,shapeOffsets=this.shapeOffsets,shapeOrientations=this.shapeOrientations,N=shapes.length,offset=tmpVec,orientation=tmpQuat$1,bodyQuat=this.quaternion,aabb=this.aabb,shapeAABB=computeAABB_shapeAABB;for(let i=0;i!==N;i++){const shape=shapes[i];bodyQuat.vmult(shapeOffsets[i],offset),offset.vadd(this.position,offset),bodyQuat.mult(shapeOrientations[i],orientation),shape.calculateWorldAABB(offset,orientation,shapeAABB.lowerBound,shapeAABB.upperBound),0===i?aabb.copy(shapeAABB):aabb.extend(shapeAABB)}this.aabbNeedsUpdate=!1}updateInertiaWorld(force){const I=this.invInertia;if(I.x!==I.y||I.y!==I.z||force){const m1=uiw_m1,m2=uiw_m2;m1.setRotationFromQuaternion(this.quaternion),m1.transpose(m2),m1.scale(I,m1),m1.mmult(m2,this.invInertiaWorld)}else;}applyForce(force,relativePoint){if(this.type!==Body.DYNAMIC)return;const rotForce=Body_applyForce_rotForce;relativePoint.cross(force,rotForce),this.force.vadd(force,this.force),this.torque.vadd(rotForce,this.torque)}applyLocalForce(localForce,localPoint){if(this.type!==Body.DYNAMIC)return;const worldForce=Body_applyLocalForce_worldForce,relativePointWorld=Body_applyLocalForce_relativePointWorld;this.vectorToWorldFrame(localForce,worldForce),this.vectorToWorldFrame(localPoint,relativePointWorld),this.applyForce(worldForce,relativePointWorld)}applyImpulse(impulse,relativePoint){if(this.type!==Body.DYNAMIC)return;const r=relativePoint,velo=Body_applyImpulse_velo;velo.copy(impulse),velo.scale(this.invMass,velo),this.velocity.vadd(velo,this.velocity);const rotVelo=Body_applyImpulse_rotVelo;r.cross(impulse,rotVelo),this.invInertiaWorld.vmult(rotVelo,rotVelo),this.angularVelocity.vadd(rotVelo,this.angularVelocity)}applyLocalImpulse(localImpulse,localPoint){if(this.type!==Body.DYNAMIC)return;const worldImpulse=Body_applyLocalImpulse_worldImpulse,relativePointWorld=Body_applyLocalImpulse_relativePoint;this.vectorToWorldFrame(localImpulse,worldImpulse),this.vectorToWorldFrame(localPoint,relativePointWorld),this.applyImpulse(worldImpulse,relativePointWorld)}updateMassProperties(){const halfExtents=Body_updateMassProperties_halfExtents;this.invMass=this.mass>0?1/this.mass:0;const I=this.inertia,fixed=this.fixedRotation;this.computeAABB(),halfExtents.set((this.aabb.upperBound.x-this.aabb.lowerBound.x)/2,(this.aabb.upperBound.y-this.aabb.lowerBound.y)/2,(this.aabb.upperBound.z-this.aabb.lowerBound.z)/2),Box.calculateInertia(halfExtents,this.mass,I),this.invInertia.set(I.x>0&&!fixed?1/I.x:0,I.y>0&&!fixed?1/I.y:0,I.z>0&&!fixed?1/I.z:0),this.updateInertiaWorld(!0)}getVelocityAtWorldPoint(worldPoint,result){const r=new Vec3;return worldPoint.vsub(this.position,r),this.angularVelocity.cross(r,result),this.velocity.vadd(result,result),result}integrate(dt,quatNormalize,quatNormalizeFast){if(this.previousPosition.copy(this.position),this.previousQuaternion.copy(this.quaternion),this.type!==Body.DYNAMIC&&this.type!==Body.KINEMATIC||this.sleepState===Body.SLEEPING)return;const velo=this.velocity,angularVelo=this.angularVelocity,pos=this.position,force=this.force,torque=this.torque,quat=this.quaternion,invMass=this.invMass,invInertia=this.invInertiaWorld,linearFactor=this.linearFactor,iMdt=invMass*dt;velo.x+=force.x*iMdt*linearFactor.x,velo.y+=force.y*iMdt*linearFactor.y,velo.z+=force.z*iMdt*linearFactor.z;const e=invInertia.elements,angularFactor=this.angularFactor,tx=torque.x*angularFactor.x,ty=torque.y*angularFactor.y,tz=torque.z*angularFactor.z;angularVelo.x+=dt*(e[0]*tx+e[1]*ty+e[2]*tz),angularVelo.y+=dt*(e[3]*tx+e[4]*ty+e[5]*tz),angularVelo.z+=dt*(e[6]*tx+e[7]*ty+e[8]*tz),pos.x+=velo.x*dt,pos.y+=velo.y*dt,pos.z+=velo.z*dt,quat.integrate(this.angularVelocity,dt,this.angularFactor,quat),quatNormalize&&(quatNormalizeFast?quat.normalizeFast():quat.normalize()),this.aabbNeedsUpdate=!0,this.updateInertiaWorld()}}exports.Body=Body,Body.COLLIDE_EVENT_NAME="collide",Body.DYNAMIC=1,Body.STATIC=2,Body.KINEMATIC=4,Body.AWAKE=BODY_SLEEP_STATES.AWAKE,Body.SLEEPY=BODY_SLEEP_STATES.SLEEPY,Body.SLEEPING=BODY_SLEEP_STATES.SLEEPING,Body.idCounter=0,Body.wakeupEvent={type:"wakeup"},Body.sleepyEvent={type:"sleepy"},Body.sleepEvent={type:"sleep"};const tmpVec=new Vec3,tmpQuat$1=new Quaternion,computeAABB_shapeAABB=new AABB,uiw_m1=new Mat3,uiw_m2=new Mat3,Body_applyForce_rotForce=new Vec3,Body_applyLocalForce_worldForce=new Vec3,Body_applyLocalForce_relativePointWorld=new Vec3,Body_applyImpulse_velo=new Vec3,Body_applyImpulse_rotVelo=new Vec3,Body_applyLocalImpulse_worldImpulse=new Vec3,Body_applyLocalImpulse_relativePoint=new Vec3,Body_updateMassProperties_halfExtents=new Vec3;class Broadphase{constructor(){this.world=null,this.useBoundingBoxes=!1,this.dirty=!0}collisionPairs(world,p1,p2){throw new Error("collisionPairs not implemented for this BroadPhase class!")}needBroadphaseCollision(bodyA,bodyB){return 0!=(bodyA.collisionFilterGroup&bodyB.collisionFilterMask)&&0!=(bodyB.collisionFilterGroup&bodyA.collisionFilterMask)&&(0==(bodyA.type&Body.STATIC)&&bodyA.sleepState!==Body.SLEEPING||0==(bodyB.type&Body.STATIC)&&bodyB.sleepState!==Body.SLEEPING)}intersectionTest(bodyA,bodyB,pairs1,pairs2){this.useBoundingBoxes?this.doBoundingBoxBroadphase(bodyA,bodyB,pairs1,pairs2):this.doBoundingSphereBroadphase(bodyA,bodyB,pairs1,pairs2)}doBoundingSphereBroadphase(bodyA,bodyB,pairs1,pairs2){const r=Broadphase_collisionPairs_r;bodyB.position.vsub(bodyA.position,r);const boundingRadiusSum2=(bodyA.boundingRadius+bodyB.boundingRadius)**2;r.lengthSquared(){const dist=new Vec3;bodyA.position.vsub(bodyB.position,dist);const sa=bodyA.shapes[0],sb=bodyB.shapes[0];return Math.pow(sa.boundingSphereRadius+sb.boundingSphereRadius,2)>dist.lengthSquared()});exports.GridBroadphase=class extends Broadphase{constructor(aabbMin=new Vec3(100,100,100),aabbMax=new Vec3(-100,-100,-100),nx=10,ny=10,nz=10){super(),this.nx=nx,this.ny=ny,this.nz=nz,this.aabbMin=aabbMin,this.aabbMax=aabbMax;const nbins=this.nx*this.ny*this.nz;if(nbins<=0)throw"GridBroadphase: Each dimension's n must be >0";this.bins=[],this.binLengths=[],this.bins.length=nbins,this.binLengths.length=nbins;for(let i=0;i=nx&&(xoff0=nx-1),yoff0<0?yoff0=0:yoff0>=ny&&(yoff0=ny-1),zoff0<0?zoff0=0:zoff0>=nz&&(zoff0=nz-1),xoff1<0?xoff1=0:xoff1>=nx&&(xoff1=nx-1),yoff1<0?yoff1=0:yoff1>=ny&&(yoff1=ny-1),zoff1<0?zoff1=0:zoff1>=nz&&(zoff1=nz-1),yoff0*=ystep,zoff0*=zstep,xoff1*=xstep,yoff1*=ystep,zoff1*=zstep;for(let xoff=xoff0*=xstep;xoff<=xoff1;xoff+=xstep)for(let yoff=yoff0;yoff<=yoff1;yoff+=ystep)for(let zoff=zoff0;zoff<=zoff1;zoff+=zstep){const idx=xoff+yoff+zoff;bins[idx][binLengths[idx]++]=bi}}for(let i=0;i!==N;i++){const bi=bodies[i],si=bi.shapes[0];switch(si.type){case SPHERE:{const shape=si,x=bi.position.x,y=bi.position.y,z=bi.position.z,r=shape.radius;addBoxToBins(x-r,y-r,z-r,x+r,y+r,z+r,bi);break}case PLANE:{const shape=si;shape.worldNormalNeedsUpdate&&shape.computeWorldNormal(bi.quaternion);const planeNormal=shape.worldNormal,xreset=xmin+.5*binsizeX-bi.position.x,yreset=ymin+.5*binsizeY-bi.position.y,zreset=zmin+.5*binsizeZ-bi.position.z,d=GridBroadphase_collisionPairs_d;d.set(xreset,yreset,zreset);for(let xi=0,xoff=0;xi!==nx;xi++,xoff+=xstep,d.y=yreset,d.x+=binsizeX)for(let yi=0,yoff=0;yi!==ny;yi++,yoff+=ystep,d.z=zreset,d.y+=binsizeY)for(let zi=0,zoff=0;zi!==nz;zi++,zoff+=zstep,d.z+=binsizeZ)if(d.dot(planeNormal)1){const bin=bins[i];for(let xi=0;xi!==binLength;xi++){const bi=bin[xi];for(let yi=0;yi!==xi;yi++){const bj=bin[yi];this.needBroadphaseCollision(bi,bj)&&this.intersectionTest(bi,bj,pairs1,pairs2)}}}}this.makePairsUnique(pairs1,pairs2)}};const GridBroadphase_collisionPairs_d=new Vec3;class NaiveBroadphase extends Broadphase{constructor(){super()}collisionPairs(world,pairs1,pairs2){const bodies=world.bodies,n=bodies.length;let bi,bj;for(let i=0;i!==n;i++)for(let j=0;j!==i;j++)bi=bodies[i],bj=bodies[j],this.needBroadphaseCollision(bi,bj)&&this.intersectionTest(bi,bj,pairs1,pairs2)}aabbQuery(world,aabb,result=[]){for(let i=0;i{})}intersectWorld(world,options){return this.mode=options.mode||Ray.ANY,this.result=options.result||new RaycastResult,this.skipBackfaces=!!options.skipBackfaces,this.collisionFilterMask=void 0!==options.collisionFilterMask?options.collisionFilterMask:-1,this.collisionFilterGroup=void 0!==options.collisionFilterGroup?options.collisionFilterGroup:-1,this.checkCollisionResponse=void 0===options.checkCollisionResponse||options.checkCollisionResponse,options.from&&this.from.copy(options.from),options.to&&this.to.copy(options.to),this.callback=options.callback||(()=>{}),this.hasHit=!1,this.result.reset(),this.updateDirection(),this.getAABB(tmpAABB),tmpArray.length=0,world.broadphase.aabbQuery(world,tmpAABB,tmpArray),this.intersectBodies(tmpArray),this.hasHit}intersectBody(body,result){result&&(this.result=result,this.updateDirection());const checkCollisionResponse=this.checkCollisionResponse;if(checkCollisionResponse&&!body.collisionResponse)return;if(0==(this.collisionFilterGroup&body.collisionFilterMask)||0==(body.collisionFilterGroup&this.collisionFilterMask))return;const xi=intersectBody_xi,qi=intersectBody_qi;for(let i=0,N=body.shapes.length;ishape.boundingSphereRadius)return;const intersectMethod=this[shape.type];intersectMethod&&intersectMethod.call(this,shape,quat,position,body,shape)}_intersectBox(box,quat,position,body,reportedShape){return this._intersectConvex(box.convexPolyhedronRepresentation,quat,position,body,reportedShape)}_intersectPlane(shape,quat,position,body,reportedShape){const from=this.from,to=this.to,direction=this.direction,worldNormal=new Vec3(0,0,1);quat.vmult(worldNormal,worldNormal);const len=new Vec3;from.vsub(position,len);const planeToFrom=len.dot(worldNormal);if(to.vsub(position,len),planeToFrom*len.dot(worldNormal)>0)return;if(from.distanceTo(to)=0&&d1<=1&&(from.lerp(to,d1,intersectionPoint),intersectionPoint.vsub(position,normal),normal.normalize(),this.reportIntersection(normal,intersectionPoint,reportedShape,body,-1)),this.result.shouldStop)return;d2>=0&&d2<=1&&(from.lerp(to,d2,intersectionPoint),intersectionPoint.vsub(position,normal),normal.normalize(),this.reportIntersection(normal,intersectionPoint,reportedShape,body,-1))}}_intersectConvex(shape,quat,position,body,reportedShape,options){const normal=intersectConvex_normal,vector=intersectConvex_vector,faceList=options&&options.faceList||null,faces=shape.faces,vertices=shape.vertices,normals=shape.faceNormals,direction=this.direction,from=this.from,to=this.to,fromToDistance=from.distanceTo(to),Nfaces=faceList?faceList.length:faces.length,result=this.result;for(let j=0;!result.shouldStop&&jfromToDistance||this.reportIntersection(normal,intersectPoint,reportedShape,body,fi)}}}}_intersectTrimesh(mesh,quat,position,body,reportedShape,options){const normal=intersectTrimesh_normal,triangles=intersectTrimesh_triangles,treeTransform=intersectTrimesh_treeTransform,vector=intersectConvex_vector,localDirection=intersectTrimesh_localDirection,localFrom=intersectTrimesh_localFrom,localTo=intersectTrimesh_localTo,worldIntersectPoint=intersectTrimesh_worldIntersectPoint,worldNormal=intersectTrimesh_worldNormal,indices=(options&&options.faceList,mesh.indices),from=(mesh.vertices,this.from),to=this.to,direction=this.direction;treeTransform.position.copy(position),treeTransform.quaternion.copy(quat),Transform.vectorToLocalFrame(position,quat,direction,localDirection),Transform.pointToLocalFrame(position,quat,from,localFrom),Transform.pointToLocalFrame(position,quat,to,localTo),localTo.x*=mesh.scale.x,localTo.y*=mesh.scale.y,localTo.z*=mesh.scale.z,localFrom.x*=mesh.scale.x,localFrom.y*=mesh.scale.y,localFrom.z*=mesh.scale.z,localTo.vsub(localFrom,localDirection),localDirection.normalize();const fromToDistanceSquared=localFrom.distanceSquared(localTo);mesh.tree.rayQuery(this,treeTransform,triangles);for(let i=0,N=triangles.length;!this.result.shouldStop&&i!==N;i++){const trianglesIndex=triangles[i];mesh.getNormal(trianglesIndex,normal),mesh.getVertex(indices[3*trianglesIndex],a),a.vsub(localFrom,vector);const dot=localDirection.dot(normal),scalar=normal.dot(vector)/dot;if(scalar<0)continue;localDirection.scale(scalar,intersectPoint),intersectPoint.vadd(localFrom,intersectPoint),mesh.getVertex(indices[3*trianglesIndex+1],b),mesh.getVertex(indices[3*trianglesIndex+2],c);const squaredDistance=intersectPoint.distanceSquared(localFrom);!pointInTriangle(intersectPoint,b,a,c)&&!pointInTriangle(intersectPoint,a,b,c)||squaredDistance>fromToDistanceSquared||(Transform.vectorToWorldFrame(quat,normal,worldNormal),Transform.pointToWorldFrame(position,quat,intersectPoint,worldIntersectPoint),this.reportIntersection(worldNormal,worldIntersectPoint,reportedShape,body,trianglesIndex))}triangles.length=0}reportIntersection(normal,hitPointWorld,shape,body,hitFaceIndex){const from=this.from,to=this.to,distance=from.distanceTo(hitPointWorld),result=this.result;if(!(this.skipBackfaces&&normal.dot(this.direction)>0))switch(result.hitFaceIndex=void 0!==hitFaceIndex?hitFaceIndex:-1,this.mode){case Ray.ALL:this.hasHit=!0,result.set(from,to,normal,hitPointWorld,shape,body,distance),result.hasHit=!0,this.callback(result);break;case Ray.CLOSEST:(distance=0&&(v=dot00*dot12-dot01*dot02)>=0&&u+v{axisList.push(event.body)}),this._removeBodyHandler=(event=>{const idx=axisList.indexOf(event.body);-1!==idx&&axisList.splice(idx,1)}),world&&this.setWorld(world)}setWorld(world){this.axisList.length=0;for(let i=0;ivarianceY?varianceX>varianceZ?0:2:varianceY>varianceZ?1:2}aabbQuery(world,aabb,result=[]){this.dirty&&(this.sortList(),this.dirty=!1);const axisIndex=this.axisIndex;let axis="x";1===axisIndex&&(axis="y"),2===axisIndex&&(axis="z");const axisList=this.axisList;aabb.lowerBound[axis],aabb.upperBound[axis];for(let i=0;i{for(let i=1,l=a.length;i=0&&!(a[j].aabb.lowerBound.x<=v.aabb.lowerBound.x);j--)a[j+1]=a[j];a[j+1]=v}return a}),SAPBroadphase.insertionSortY=(a=>{for(let i=1,l=a.length;i=0&&!(a[j].aabb.lowerBound.y<=v.aabb.lowerBound.y);j--)a[j+1]=a[j];a[j+1]=v}return a}),SAPBroadphase.insertionSortZ=(a=>{for(let i=1,l=a.length;i=0&&!(a[j].aabb.lowerBound.z<=v.aabb.lowerBound.z);j--)a[j+1]=a[j];a[j+1]=v}return a}),SAPBroadphase.checkBounds=((bi,bj,axisIndex)=>{let biPos,bjPos;0===axisIndex?(biPos=bi.position.x,bjPos=bj.position.x):1===axisIndex?(biPos=bi.position.y,bjPos=bj.position.y):2===axisIndex&&(biPos=bi.position.z,bjPos=bj.position.z);const ri=bi.boundingRadius;return bjPos-bj.boundingRadius{for(let key in defaults)key in options||(options[key]=defaults[key]);return options});class Constraint{constructor(bodyA,bodyB,options={}){options=Utils.defaults(options,{collideConnected:!0,wakeUpBodies:!0}),this.equations=[],this.bodyA=bodyA,this.bodyB=bodyB,this.id=Constraint.idCounter++,this.collideConnected=options.collideConnected,options.wakeUpBodies&&(bodyA&&bodyA.wakeUp(),bodyB&&bodyB.wakeUp())}update(){throw new Error("method update() not implmemented in this Constraint subclass!")}enable(){const eqs=this.equations;for(let i=0;i=-.1)this.suspensionRelativeVelocity=0,this.clippedInvContactDotSuspension=10;else{const inv=-1/project;this.suspensionRelativeVelocity=projVel*inv,this.clippedInvContactDotSuspension=inv}}else raycastResult.suspensionLength=this.suspensionRestLength,this.suspensionRelativeVelocity=0,raycastResult.directionWorld.scale(-1,raycastResult.hitNormalWorld),this.clippedInvContactDotSuspension=1}}const chassis_velocity_at_contactPoint=new Vec3,relpos=new Vec3;exports.RaycastVehicle=class{constructor(options){this.chassisBody=options.chassisBody,this.wheelInfos=[],this.sliding=!1,this.world=null,this.indexRightAxis=void 0!==options.indexRightAxis?options.indexRightAxis:1,this.indexForwardAxis=void 0!==options.indexForwardAxis?options.indexForwardAxis:0,this.indexUpAxis=void 0!==options.indexUpAxis?options.indexUpAxis:2,this.constraints=[],this.preStepCallback=(()=>{}),this.currentVehicleSpeedKmHour=0}addWheel(options={}){const info=new WheelInfo(options),index=this.wheelInfos.length;return this.wheelInfos.push(info),index}setSteeringValue(value,wheelIndex){this.wheelInfos[wheelIndex].steering=value}applyEngineForce(value,wheelIndex){this.wheelInfos[wheelIndex].engineForce=value}setBrake(brake,wheelIndex){this.wheelInfos[wheelIndex].brake=brake}addToWorld(world){this.constraints,world.addBody(this.chassisBody);const that=this;this.preStepCallback=(()=>{that.updateVehicle(world.dt)}),world.addEventListener("preStep",this.preStepCallback),this.world=world}getVehicleAxisWorld(axisIndex,result){result.set(0===axisIndex?1:0,1===axisIndex?1:0,2===axisIndex?1:0),this.chassisBody.vectorToWorldFrame(result,result)}updateVehicle(timeStep){const wheelInfos=this.wheelInfos,numWheels=wheelInfos.length,chassisBody=this.chassisBody;for(let i=0;iwheel.maxSuspensionForce&&(suspensionForce=wheel.maxSuspensionForce),wheel.raycastResult.hitNormalWorld.scale(suspensionForce*timeStep,impulse),wheel.raycastResult.hitPointWorld.vsub(chassisBody.position,relpos),chassisBody.applyImpulse(impulse,relpos)}this.updateFriction(timeStep);const hitNormalWorldScaledWithProj=new Vec3,fwd=new Vec3,vel=new Vec3;for(let i=0;i0?1:-1)*wheel.customSlidingRotationalSpeed*timeStep),Math.abs(wheel.brake)>Math.abs(wheel.engineForce)&&(wheel.deltaRotation=0),wheel.rotation+=wheel.deltaRotation,wheel.deltaRotation*=.99}}updateSuspension(deltaTime){const chassisMass=this.chassisBody.mass,wheelInfos=this.wheelInfos,numWheels=wheelInfos.length;for(let w_it=0;w_itmaxSuspensionLength&&(wheel.suspensionLength=maxSuspensionLength,wheel.raycastResult.reset());const denominator=wheel.raycastResult.hitNormalWorld.dot(wheel.directionWorld),chassis_velocity_at_contactPoint=new Vec3;chassisBody.getVelocityAtWorldPoint(wheel.raycastResult.hitPointWorld,chassis_velocity_at_contactPoint);const projVel=wheel.raycastResult.hitNormalWorld.dot(chassis_velocity_at_contactPoint);if(denominator>=-.1)wheel.suspensionRelativeVelocity=0,wheel.clippedInvContactDotSuspension=10;else{const inv=-1/denominator;wheel.suspensionRelativeVelocity=projVel*inv,wheel.clippedInvContactDotSuspension=inv}}else wheel.suspensionLength=wheel.suspensionRestLength+0*wheel.maxSuspensionTravel,wheel.suspensionRelativeVelocity=0,wheel.directionWorld.scale(-1,wheel.raycastResult.hitNormalWorld),wheel.clippedInvContactDotSuspension=1;return depth}updateWheelTransformWorld(wheel){wheel.isInContact=!1;const chassisBody=this.chassisBody;chassisBody.pointToWorldFrame(wheel.chassisConnectionPointLocal,wheel.chassisConnectionPointWorld),chassisBody.vectorToWorldFrame(wheel.directionLocal,wheel.directionWorld),chassisBody.vectorToWorldFrame(wheel.axleLocal,wheel.axleWorld)}updateWheelTransform(wheelIndex){const up=tmpVec4,right=tmpVec5,fwd=tmpVec6,wheel=this.wheelInfos[wheelIndex];this.updateWheelTransformWorld(wheel),wheel.directionLocal.scale(-1,up),right.copy(wheel.axleLocal),up.cross(right,fwd),fwd.normalize(),right.normalize();const steering=wheel.steering,steeringOrn=new Quaternion;steeringOrn.setFromAxisAngle(up,steering);const rotatingOrn=new Quaternion;rotatingOrn.setFromAxisAngle(right,wheel.rotation);const q=wheel.worldTransform.quaternion;this.chassisBody.quaternion.mult(steeringOrn,q),q.mult(rotatingOrn,q),q.normalize();const p=wheel.worldTransform.position;p.copy(wheel.directionWorld),p.scale(wheel.suspensionLength,p),p.vadd(wheel.chassisConnectionPointWorld,p)}getWheelTransformWorld(wheelIndex){return this.wheelInfos[wheelIndex].worldTransform}updateFriction(timeStep){const surfNormalWS_scaled_proj=updateFriction_surfNormalWS_scaled_proj,wheelInfos=this.wheelInfos,numWheels=wheelInfos.length,chassisBody=this.chassisBody,forwardWS=updateFriction_forwardWS,axle=updateFriction_axle;for(let i=0;imaximpSquared){this.sliding=!0,wheel.sliding=!0;const factor=maximp/Math.sqrt(impulseSquared);wheel.skidInfo*=factor}}}if(this.sliding)for(let i=0;i1.1)return 0;const vel1=resolveSingleBilateral_vel1,vel2=resolveSingleBilateral_vel2,vel=resolveSingleBilateral_vel;body1.getVelocityAtWorldPoint(pos1,vel1),body2.getVelocityAtWorldPoint(pos2,vel2),vel1.vsub(vel2,vel);return-.2*normal.dot(vel)*(1/(body1.invMass+body2.invMass))}class Sphere extends Shape{constructor(radius){if(super({type:Shape.types.SPHERE}),this.radius=void 0!==radius?radius:1,this.radius<0)throw new Error("The sphere radius cannot be negative.");this.updateBoundingSphereRadius()}calculateLocalInertia(mass,target=new Vec3){const I=2*mass*this.radius*this.radius/5;return target.x=I,target.y=I,target.z=I,target}volume(){return 4*Math.PI*Math.pow(this.radius,3)/3}updateBoundingSphereRadius(){this.boundingSphereRadius=this.radius}calculateWorldAABB(pos,quat,min,max){const r=this.radius,axes=["x","y","z"];for(let i=0;ithis.particles.length&&this.neighbors.pop())}getNeighbors(particle,neighbors){const N=this.particles.length,id=particle.id,R2=this.smoothingRadius*this.smoothingRadius,dist=SPHSystem_getNeighbors_dist;for(let i=0;i!==N;i++){const p=this.particles[i];p.position.vsub(particle.position,dist),id!==p.id&&dist.lengthSquared()maxValue&&(maxValue=v)}this.maxValue=maxValue}setHeightValueAtIndex(xi,yi,value){this.data[xi][yi]=value,this.clearCachedConvexTrianglePillar(xi,yi,!1),xi>0&&(this.clearCachedConvexTrianglePillar(xi-1,yi,!0),this.clearCachedConvexTrianglePillar(xi-1,yi,!1)),yi>0&&(this.clearCachedConvexTrianglePillar(xi,yi-1,!0),this.clearCachedConvexTrianglePillar(xi,yi-1,!1)),yi>0&&xi>0&&this.clearCachedConvexTrianglePillar(xi-1,yi-1,!0)}getRectMinMax(iMinX,iMinY,iMaxX,iMaxY,result=[]){const data=this.data;let max=this.minValue;for(let i=iMinX;i<=iMaxX;i++)for(let j=iMinY;j<=iMaxY;j++){const height=data[i][j];height>max&&(max=height)}result[0]=this.minValue,result[1]=max}getIndexOfPosition(x,y,result,clamp){const w=this.elementSize,data=this.data;let xi=Math.floor(x/w),yi=Math.floor(y/w);return result[0]=xi,result[1]=yi,clamp&&(xi<0&&(xi=0),yi<0&&(yi=0),xi>=data.length-1&&(xi=data.length-1),yi>=data[0].length-1&&(yi=data[0].length-1)),!(xi<0||yi<0||xi>=data.length-1||yi>=data[0].length-1)}getTriangleAt(x,y,edgeClamp,a,b,c){const idx=getHeightAt_idx;this.getIndexOfPosition(x,y,idx,edgeClamp);let xi=idx[0],yi=idx[1];const data=this.data;edgeClamp&&(xi=Math.min(data.length-2,Math.max(0,xi)),yi=Math.min(data[0].length-2,Math.max(0,yi)));const elementSize=this.elementSize,upper=(x/elementSize-xi)**2+(y/elementSize-yi)**2>(x/elementSize-(xi+1))**2+(y/elementSize-(yi+1))**2;return this.getTriangle(xi,yi,upper,a,b,c),upper}getNormalAt(x,y,edgeClamp,result){const a=getNormalAt_a,b=getNormalAt_b,c=getNormalAt_c,e0=getNormalAt_e0,e1=getNormalAt_e1;this.getTriangleAt(x,y,edgeClamp,a,b,c),b.vsub(a,e0),c.vsub(a,e1),e0.cross(e1,result),result.normalize()}getAabbAtIndex(xi,yi,{lowerBound:lowerBound,upperBound:upperBound}){const data=this.data,elementSize=this.elementSize;lowerBound.set(xi*elementSize,yi*elementSize,data[xi][yi]),upperBound.set((xi+1)*elementSize,(yi+1)*elementSize,data[xi+1][yi+1])}getHeightAt(x,y,edgeClamp){const data=this.data,a=getHeightAt_a,b=getHeightAt_b,c=getHeightAt_c,idx=getHeightAt_idx;this.getIndexOfPosition(x,y,idx,edgeClamp);let xi=idx[0],yi=idx[1];edgeClamp&&(xi=Math.min(data.length-2,Math.max(0,xi)),yi=Math.min(data[0].length-2,Math.max(0,yi)));const upper=this.getTriangleAt(x,y,edgeClamp,a,b,c);!function(x,y,ax,ay,bx,by,cx,cy,result){result.x=((by-cy)*(x-cx)+(cx-bx)*(y-cy))/((by-cy)*(ax-cx)+(cx-bx)*(ay-cy)),result.y=((cy-ay)*(x-cx)+(ax-cx)*(y-cy))/((by-cy)*(ax-cx)+(cx-bx)*(ay-cy)),result.z=1-result.x-result.y}(x,y,a.x,a.y,b.x,b.y,c.x,c.y,getHeightAt_weights);const w=getHeightAt_weights;return upper?data[xi+1][yi+1]*w.x+data[xi][yi+1]*w.y+data[xi+1][yi]*w.z:data[xi][yi]*w.x+data[xi+1][yi]*w.y+data[xi][yi+1]*w.z}getCacheConvexTrianglePillarKey(xi,yi,getUpperTriangle){return xi+"_"+yi+"_"+(getUpperTriangle?1:0)}getCachedConvexTrianglePillar(xi,yi,getUpperTriangle){return this._cachedPillars[this.getCacheConvexTrianglePillarKey(xi,yi,getUpperTriangle)]}setCachedConvexTrianglePillar(xi,yi,getUpperTriangle,convex,offset){this._cachedPillars[this.getCacheConvexTrianglePillarKey(xi,yi,getUpperTriangle)]={convex:convex,offset:offset}}clearCachedConvexTrianglePillar(xi,yi,getUpperTriangle){delete this._cachedPillars[this.getCacheConvexTrianglePillarKey(xi,yi,getUpperTriangle)]}getTriangle(xi,yi,upper,a,b,c){const data=this.data,elementSize=this.elementSize;upper?(a.set((xi+1)*elementSize,(yi+1)*elementSize,data[xi+1][yi+1]),b.set(xi*elementSize,(yi+1)*elementSize,data[xi][yi+1]),c.set((xi+1)*elementSize,yi*elementSize,data[xi+1][yi])):(a.set(xi*elementSize,yi*elementSize,data[xi][yi]),b.set((xi+1)*elementSize,yi*elementSize,data[xi+1][yi]),c.set(xi*elementSize,(yi+1)*elementSize,data[xi][yi+1]))}getConvexTrianglePillar(xi,yi,getUpperTriangle){let result=this.pillarConvex,offsetResult=this.pillarOffset;if(this.cacheEnabled){const data=this.getCachedConvexTrianglePillar(xi,yi,getUpperTriangle);if(data)return this.pillarConvex=data.convex,void(this.pillarOffset=data.offset);result=new ConvexPolyhedron,offsetResult=new Vec3,this.pillarConvex=result,this.pillarOffset=offsetResult}const data=this.data,elementSize=this.elementSize,faces=result.faces;result.vertices.length=6;for(let i=0;i<6;i++)result.vertices[i]||(result.vertices[i]=new Vec3);faces.length=5;for(let i=0;i<5;i++)faces[i]||(faces[i]=[]);const verts=result.vertices,h=(Math.min(data[xi][yi],data[xi+1][yi],data[xi][yi+1],data[xi+1][yi+1])-this.minValue)/2+this.minValue;getUpperTriangle?(offsetResult.set((xi+.75)*elementSize,(yi+.75)*elementSize,h),verts[0].set(.25*elementSize,.25*elementSize,data[xi+1][yi+1]-h),verts[1].set(-.75*elementSize,.25*elementSize,data[xi][yi+1]-h),verts[2].set(.25*elementSize,-.75*elementSize,data[xi+1][yi]-h),verts[3].set(.25*elementSize,.25*elementSize,-h-1),verts[4].set(-.75*elementSize,.25*elementSize,-h-1),verts[5].set(.25*elementSize,-.75*elementSize,-h-1),faces[0][0]=0,faces[0][1]=1,faces[0][2]=2,faces[1][0]=5,faces[1][1]=4,faces[1][2]=3,faces[2][0]=2,faces[2][1]=5,faces[2][2]=3,faces[2][3]=0,faces[3][0]=3,faces[3][1]=4,faces[3][2]=1,faces[3][3]=0,faces[4][0]=1,faces[4][1]=4,faces[4][2]=5,faces[4][3]=2):(offsetResult.set((xi+.25)*elementSize,(yi+.25)*elementSize,h),verts[0].set(-.25*elementSize,-.25*elementSize,data[xi][yi]-h),verts[1].set(.75*elementSize,-.25*elementSize,data[xi+1][yi]-h),verts[2].set(-.25*elementSize,.75*elementSize,data[xi][yi+1]-h),verts[3].set(-.25*elementSize,-.25*elementSize,-h-1),verts[4].set(.75*elementSize,-.25*elementSize,-h-1),verts[5].set(-.25*elementSize,.75*elementSize,-h-1),faces[0][0]=0,faces[0][1]=1,faces[0][2]=2,faces[1][0]=5,faces[1][1]=4,faces[1][2]=3,faces[2][0]=0,faces[2][1]=2,faces[2][2]=5,faces[2][3]=3,faces[3][0]=1,faces[3][1]=0,faces[3][2]=3,faces[3][3]=4,faces[4][0]=4,faces[4][1]=5,faces[4][2]=2,faces[4][3]=1),result.computeNormals(),result.computeEdges(),result.updateBoundingSphereRadius(),this.setCachedConvexTrianglePillar(xi,yi,getUpperTriangle,result,offsetResult)}calculateLocalInertia(mass,target=new Vec3){return target.set(0,0,0),target}volume(){return Number.MAX_VALUE}calculateWorldAABB(pos,quat,min,max){min.set(-Number.MAX_VALUE,-Number.MAX_VALUE,-Number.MAX_VALUE),max.set(Number.MAX_VALUE,Number.MAX_VALUE,Number.MAX_VALUE)}updateBoundingSphereRadius(){const data=this.data,s=this.elementSize;this.boundingSphereRadius=new Vec3(data.length*s,data[0].length*s,Math.max(Math.abs(this.maxValue),Math.abs(this.minValue))).length()}setHeightsFromImage(image,scale){const{x:x,z:z,y:y}=scale,canvas=document.createElement("canvas");canvas.width=image.width,canvas.height=image.height;const context=canvas.getContext("2d");context.drawImage(image,0,0);const imageData=context.getImageData(0,0,image.width,image.height),matrix=this.data;matrix.length=0,this.elementSize=Math.abs(x)/imageData.width;for(let i=0;i=0;i--)this.children[i].removeEmptyNodes(),this.children[i].children.length||this.children[i].data.length||this.children.splice(i,1)}}class Octree extends OctreeNode{constructor(aabb,options={}){super({root:null,aabb:aabb}),this.maxDepth=void 0!==options.maxDepth?options.maxDepth:8}}const halfDiagonal=new Vec3,tmpAABB$1=new AABB;class Trimesh extends Shape{constructor(vertices,indices){super({type:Shape.types.TRIMESH}),this.vertices=new Float32Array(vertices),this.indices=new Int16Array(indices),this.normals=new Float32Array(indices.length),this.aabb=new AABB,this.edges=null,this.scale=new Vec3(1,1,1),this.tree=new Octree,this.updateEdges(),this.updateNormals(),this.updateAABB(),this.updateBoundingSphereRadius(),this.updateTree()}updateTree(){const tree=this.tree;tree.reset(),tree.aabb.copy(this.aabb);const scale=this.scale;tree.aabb.lowerBound.x*=1/scale.x,tree.aabb.lowerBound.y*=1/scale.y,tree.aabb.lowerBound.z*=1/scale.z,tree.aabb.upperBound.x*=1/scale.x,tree.aabb.upperBound.y*=1/scale.y,tree.aabb.upperBound.z*=1/scale.z;const triangleAABB=new AABB,a=new Vec3,b=new Vec3,c=new Vec3,points=[a,b,c];for(let i=0;i{edges[au.x&&(u.x=v.x),v.yu.y&&(u.y=v.y),v.zu.z&&(u.z=v.z)}updateAABB(){this.computeLocalAABB(this.aabb)}updateBoundingSphereRadius(){let max2=0;const vertices=this.vertices,v=new Vec3;for(let i=0,N=vertices.length/3;i!==N;i++){this.getVertex(i,v);const norm2=v.lengthSquared();norm2>max2&&(max2=norm2)}this.boundingSphereRadius=Math.sqrt(max2)}calculateWorldAABB(pos,quat,min,max){const frame=calculateWorldAABB_frame,result=calculateWorldAABB_aabb;frame.position=pos,frame.quaternion=quat,this.aabb.toWorldFrame(frame,result),min.copy(result.lowerBound),max.copy(result.upperBound)}volume(){return 4*Math.PI*this.boundingSphereRadius/3}}exports.Trimesh=Trimesh;const computeNormals_n=new Vec3,unscaledAABB=new AABB,getEdgeVector_va=new Vec3,getEdgeVector_vb=new Vec3,cb=new Vec3,ab=new Vec3;Trimesh.computeNormal=((va,vb,vc,target)=>{vb.vsub(va,ab),vc.vsub(vb,cb),cb.cross(ab,target),target.isZero()||target.normalize()});const va=new Vec3,vb=new Vec3,vc=new Vec3,cli_aabb=new AABB,computeLocalAABB_worldVert=new Vec3,calculateWorldAABB_frame=new Transform,calculateWorldAABB_aabb=new AABB;Trimesh.createTorus=((radius=1,tube=.5,radialSegments=8,tubularSegments=6,arc=2*Math.PI)=>{const vertices=[],indices=[];for(let j=0;j<=radialSegments;j++)for(let i=0;i<=tubularSegments;i++){const u=i/tubularSegments*arc,v=j/radialSegments*Math.PI*2,x=(radius+tube*Math.cos(v))*Math.cos(u),y=(radius+tube*Math.cos(v))*Math.sin(u),z=tube*Math.sin(v);vertices.push(x,y,z)}for(let j=1;j<=radialSegments;j++)for(let i=1;i<=tubularSegments;i++){const a=(tubularSegments+1)*j+i-1,b=(tubularSegments+1)*(j-1)+i-1,c=(tubularSegments+1)*(j-1)+i,d=(tubularSegments+1)*j+i;indices.push(a,b,d),indices.push(b,c,d)}return new Trimesh(vertices,indices)});class Solver{constructor(){this.equations=[]}solve(dt,world){return 0}addEquation(eq){eq.enabled&&this.equations.push(eq)}removeEquation(eq){const eqs=this.equations,i=eqs.indexOf(eq);-1!==i&&eqs.splice(i,1)}removeAllEquations(){this.equations.length=0}}exports.Solver=Solver;class GSSolver extends Solver{constructor(){super(),this.iterations=10,this.tolerance=1e-7}solve(dt,world){let iter=0;const maxIter=this.iterations,tolSquared=this.tolerance*this.tolerance,equations=this.equations,Neq=equations.length,bodies=world.bodies,Nbodies=bodies.length,h=dt;let B,invC,deltalambda,deltalambdaTot,GWlambda,lambdaj;if(0!==Neq)for(let i=0;i!==Nbodies;i++)bodies[i].updateSolveMassProperties();const invCs=GSSolver_solve_invCs,Bs=GSSolver_solve_Bs,lambda=GSSolver_solve_lambda;invCs.length=Neq,Bs.length=Neq,lambda.length=Neq;for(let i=0;i!==Neq;i++){const c=equations[i];lambda[i]=0,Bs[i]=c.computeB(h),invCs[i]=1/c.computeC()}if(0!==Neq){for(let i=0;i!==Nbodies;i++){const b=bodies[i],vlambda=b.vlambda,wlambda=b.wlambda;vlambda.set(0,0,0),wlambda.set(0,0,0)}for(iter=0;iter!==maxIter;iter++){deltalambdaTot=0;for(let j=0;j!==Neq;j++){const c=equations[j];B=Bs[j],invC=invCs[j],(lambdaj=lambda[j])+(deltalambda=invC*(B-(GWlambda=c.computeGWlambda())-c.eps*lambdaj))c.maxForce&&(deltalambda=c.maxForce-lambdaj),lambda[j]+=deltalambda,deltalambdaTot+=deltalambda>0?deltalambda:-deltalambda,c.addToWlambda(deltalambda)}if(deltalambdaTot*deltalambdaTotsize;)objects.pop();for(;objects.length=0&&matB.restitution>=0&&(c.restitution=matA.restitution*matB.restitution),c.si=overrideShapeA||si,c.sj=overrideShapeB||sj,c}createFrictionEquationsFromContact(contactEquation,outArray){const bodyA=contactEquation.bi,bodyB=contactEquation.bj,shapeA=contactEquation.si,shapeB=contactEquation.sj,world=this.world,cm=this.currentContactMaterial;let friction=cm.friction;const matA=shapeA.material||bodyA.material,matB=shapeB.material||bodyB.material;if(matA&&matB&&matA.friction>=0&&matB.friction>=0&&(friction=matA.friction*matB.friction),friction>0){const mug=friction*world.gravity.length();let reducedMass=bodyA.invMass+bodyB.invMass;reducedMass>0&&(reducedMass=1/reducedMass);const pool=this.frictionEquationPool,c1=pool.length?pool.pop():new FrictionEquation(bodyA,bodyB,mug*reducedMass),c2=pool.length?pool.pop():new FrictionEquation(bodyA,bodyB,mug*reducedMass);return c1.bi=c2.bi=bodyA,c1.bj=c2.bj=bodyB,c1.minForce=c2.minForce=-mug*reducedMass,c1.maxForce=c2.maxForce=mug*reducedMass,c1.ri.copy(contactEquation.ri),c1.rj.copy(contactEquation.rj),c2.ri.copy(contactEquation.ri),c2.rj.copy(contactEquation.rj),contactEquation.ni.tangents(c1.t,c2.t),c1.setSpookParams(cm.frictionEquationStiffness,cm.frictionEquationRelaxation,world.dt),c2.setSpookParams(cm.frictionEquationStiffness,cm.frictionEquationRelaxation,world.dt),c1.enabled=c2.enabled=contactEquation.enabled,outArray.push(c1,c2),!0}return!1}createFrictionFromAverage(numContacts){let c=this.result[this.result.length-1];if(!this.createFrictionEquationsFromContact(c,this.frictionResult)||1===numContacts)return;const f1=this.frictionResult[this.frictionResult.length-2],f2=this.frictionResult[this.frictionResult.length-1];averageNormal.setZero(),averageContactPointA.setZero(),averageContactPointB.setZero();const bodyA=c.bi;c.bj;for(let i=0;i!==numContacts;i++)(c=this.result[this.result.length-1-i]).bi!==bodyA?(averageNormal.vadd(c.ni,averageNormal),averageContactPointA.vadd(c.ri,averageContactPointA),averageContactPointB.vadd(c.rj,averageContactPointB)):(averageNormal.vsub(c.ni,averageNormal),averageContactPointA.vadd(c.rj,averageContactPointA),averageContactPointB.vadd(c.ri,averageContactPointB));const invNumContacts=1/numContacts;averageContactPointA.scale(invNumContacts,f1.ri),averageContactPointB.scale(invNumContacts,f1.rj),f2.ri.copy(f1.ri),f2.rj.copy(f1.rj),averageNormal.normalize(),averageNormal.tangents(f1.t,f2.t)}getContacts(p1,p2,world,result,oldcontacts,frictionResult,frictionPool){this.contactPointPool=oldcontacts,this.frictionEquationPool=frictionPool,this.result=result,this.frictionResult=frictionResult;const qi=tmpQuat1,qj=tmpQuat2,xi=tmpVec1$2,xj=tmpVec2$2;for(let k=0,N=p1.length;k!==N;k++){const bi=p1[k],bj=p2[k];let bodyContactMaterial=null;bi.material&&bj.material&&(bodyContactMaterial=world.getContactMaterial(bi.material,bj.material)||null);const justTest=bi.type&Body.KINEMATIC&&bj.type&Body.STATIC||bi.type&Body.STATIC&&bj.type&Body.KINEMATIC||bi.type&Body.KINEMATIC&&bj.type&Body.KINEMATIC;for(let i=0;isi.boundingSphereRadius+sj.boundingSphereRadius)continue;let shapeContactMaterial=null;si.material&&sj.material&&(shapeContactMaterial=world.getContactMaterial(si.material,sj.material)||null),this.currentContactMaterial=shapeContactMaterial||bodyContactMaterial||world.defaultContactMaterial;const resolver=this[si.type|sj.type];if(resolver){let retval=!1;(retval=si.type0){const ns1=sphereBox_ns1,ns2=sphereBox_ns2;ns1.copy(sides[(idx+1)%3]),ns2.copy(sides[(idx+2)%3]);const h1=ns1.length(),h2=ns2.length();ns1.normalize(),ns2.normalize();const dot1=box_to_sphere.dot(ns1),dot2=box_to_sphere.dot(ns2);if(dot1-h1&&dot2-h2){const dist=Math.abs(dot-h-R);if((null===side_distance||distsi.boundingSphereRadius+sj.boundingSphereRadius)&&si.findSeparatingAxis(sj,xi,qi,xj,qj,sepAxis,faceListA,faceListB)){const res=[],q=convexConvex_q;si.clipAgainstHull(xi,qi,sj,xj,qj,sepAxis,-100,100,res);let numContacts=0;for(let j=0;j!==res.length;j++){if(justTest)return!0;const r=this.createContactEquation(bi,bj,si,sj,rsi,rsj),ri=r.ri,rj=r.rj;sepAxis.negate(r.ni),res[j].normal.negate(q),q.scale(res[j].depth,q),res[j].point.vadd(q,ri),rj.copy(res[j].point),ri.vsub(xi,ri),rj.vsub(xj,rj),ri.vadd(xi,ri),ri.vsub(bi.position,ri),rj.vadd(xj,rj),rj.vsub(bj.position,rj),this.result.push(r),numContacts++,this.enableFrictionReduction||this.createFrictionEquationsFromContact(r,this.frictionResult)}this.enableFrictionReduction&&numContacts&&this.createFrictionFromAverage(numContacts)}}sphereConvex(si,sj,xi,xj,qi,qj,bi,bj,rsi,rsj,justTest){const v3pool=this.v3pool;xi.vsub(xj,convex_to_sphere);const normals=sj.faceNormals,faces=sj.faces,verts=sj.vertices,R=si.radius;let found=!1;for(let i=0;i!==verts.length;i++){const v=verts[i],worldCorner=sphereConvex_worldCorner;qj.vmult(v,worldCorner),xj.vadd(worldCorner,worldCorner);const sphere_to_corner=sphereConvex_sphereToCorner;if(worldCorner.vsub(xi,sphere_to_corner),sphere_to_corner.lengthSquared()0){const faceVerts=[];for(let j=0,Nverts=face.length;j!==Nverts;j++){const worldVertex=v3pool.get();qj.vmult(verts[face[j]],worldVertex),xj.vadd(worldVertex,worldVertex),faceVerts.push(worldVertex)}if(pointInPolygon(faceVerts,worldNormal,xi)){if(justTest)return!0;found=!0;const r=this.createContactEquation(bi,bj,si,sj,rsi,rsj);worldNormal.scale(-R,r.ri),worldNormal.negate(r.ni);const penetrationVec2=v3pool.get();worldNormal.scale(-penetration,penetrationVec2);const penetrationSpherePoint=v3pool.get();worldNormal.scale(-R,penetrationSpherePoint),xi.vsub(xj,r.rj),r.rj.vadd(penetrationSpherePoint,r.rj),r.rj.vadd(penetrationVec2,r.rj),r.rj.vadd(xj,r.rj),r.rj.vsub(bj.position,r.rj),r.ri.vadd(xi,r.ri),r.ri.vsub(bi.position,r.ri),v3pool.release(penetrationVec2),v3pool.release(penetrationSpherePoint),this.result.push(r),this.createFrictionEquationsFromContact(r,this.frictionResult);for(let j=0,Nfaceverts=faceVerts.length;j!==Nfaceverts;j++)v3pool.release(faceVerts[j]);return}for(let j=0;j!==face.length;j++){const v1=v3pool.get(),v2=v3pool.get();qj.vmult(verts[face[(j+1)%face.length]],v1),qj.vmult(verts[face[(j+2)%face.length]],v2),xj.vadd(v1,v1),xj.vadd(v2,v2);const edge=sphereConvex_edge;v2.vsub(v1,edge);const edgeUnit=sphereConvex_edgeUnit;edge.unit(edgeUnit);const p=v3pool.get(),v1_to_xi=v3pool.get();xi.vsub(v1,v1_to_xi);const dot=v1_to_xi.dot(edgeUnit);edgeUnit.scale(dot,p),p.vadd(v1,p);const xi_to_p=v3pool.get();if(p.vsub(xi,xi_to_p),dot>0&&dot*dotdata.length||iMinY>data[0].length)return;iMinX<0&&(iMinX=0),iMaxX<0&&(iMaxX=0),iMinY<0&&(iMinY=0),iMaxY<0&&(iMaxY=0),iMinX>=data.length&&(iMinX=data.length-1),iMaxX>=data.length&&(iMaxX=data.length-1),iMaxY>=data[0].length&&(iMaxY=data[0].length-1),iMinY>=data[0].length&&(iMinY=data[0].length-1);const minMax=[];hfShape.getRectMinMax(iMinX,iMinY,iMaxX,iMaxY,minMax);const min=minMax[0],max=minMax[1];if(localSpherePos.z-radius>max||localSpherePos.z+radius2)return}}boxHeightfield(si,sj,xi,xj,qi,qj,bi,bj,rsi,rsj,justTest){return si.convexPolyhedronRepresentation.material=si.material,si.convexPolyhedronRepresentation.collisionResponse=si.collisionResponse,this.convexHeightfield(si.convexPolyhedronRepresentation,sj,xi,xj,qi,qj,bi,bj,si,sj,justTest)}convexHeightfield(convexShape,hfShape,convexPos,hfPos,convexQuat,hfQuat,convexBody,hfBody,rsi,rsj,justTest){const data=hfShape.data,w=hfShape.elementSize,radius=convexShape.boundingSphereRadius,worldPillarOffset=convexHeightfield_tmp2,faceList=convexHeightfield_faceList,localConvexPos=convexHeightfield_tmp1;Transform.pointToLocalFrame(hfPos,hfQuat,convexPos,localConvexPos);let iMinX=Math.floor((localConvexPos.x-radius)/w)-1,iMaxX=Math.ceil((localConvexPos.x+radius)/w)+1,iMinY=Math.floor((localConvexPos.y-radius)/w)-1,iMaxY=Math.ceil((localConvexPos.y+radius)/w)+1;if(iMaxX<0||iMaxY<0||iMinX>data.length||iMinY>data[0].length)return;iMinX<0&&(iMinX=0),iMaxX<0&&(iMaxX=0),iMinY<0&&(iMinY=0),iMaxY<0&&(iMaxY=0),iMinX>=data.length&&(iMinX=data.length-1),iMaxX>=data.length&&(iMaxX=data.length-1),iMaxY>=data[0].length&&(iMaxY=data[0].length-1),iMinY>=data[0].length&&(iMinY=data[0].length-1);const minMax=[];hfShape.getRectMinMax(iMinX,iMinY,iMaxX,iMaxY,minMax);const min=minMax[0],max=minMax[1];if(!(localConvexPos.z-radius>max||localConvexPos.z+radius0&&positionAlongEdgeB<0){if(localSpherePos.vsub(edgeVertexA,tmp),edgeVectorUnit.copy(edgeVector),edgeVectorUnit.normalize(),positionAlongEdgeA=tmp.dot(edgeVectorUnit),edgeVectorUnit.scale(positionAlongEdgeA,tmp),tmp.vadd(edgeVertexA,tmp),tmp.distanceTo(localSpherePos)0&&!0===positiveResult||r<=0&&!1===positiveResult))return!1;null===positiveResult&&(positiveResult=r>0)}return!0}const box_to_sphere=new Vec3,sphereBox_ns=new Vec3,sphereBox_ns1=new Vec3,sphereBox_ns2=new Vec3,sphereBox_sides=[new Vec3,new Vec3,new Vec3,new Vec3,new Vec3,new Vec3],sphereBox_sphere_to_corner=new Vec3,sphereBox_side_ns=new Vec3,sphereBox_side_ns1=new Vec3,sphereBox_side_ns2=new Vec3;Narrowphase.prototype[COLLISION_TYPES.sphereBox]=Narrowphase.prototype.sphereBox;const convex_to_sphere=new Vec3,sphereConvex_edge=new Vec3,sphereConvex_edgeUnit=new Vec3,sphereConvex_sphereToCorner=new Vec3,sphereConvex_worldCorner=new Vec3,sphereConvex_worldNormal=new Vec3,sphereConvex_worldPoint=new Vec3,sphereConvex_worldSpherePointClosestToPlane=new Vec3,sphereConvex_penetrationVec=new Vec3,sphereConvex_sphereToWorldPoint=new Vec3;Narrowphase.prototype[COLLISION_TYPES.sphereConvex]=Narrowphase.prototype.sphereConvex,Narrowphase.prototype[COLLISION_TYPES.planeBox]=Narrowphase.prototype.planeBox;const planeConvex_v=new Vec3,planeConvex_normal=new Vec3,planeConvex_relpos=new Vec3,planeConvex_projected=new Vec3;Narrowphase.prototype[COLLISION_TYPES.planeConvex]=Narrowphase.prototype.planeConvex;const convexConvex_sepAxis=new Vec3,convexConvex_q=new Vec3;Narrowphase.prototype[COLLISION_TYPES.convexConvex]=Narrowphase.prototype.convexConvex;const particlePlane_normal=new Vec3,particlePlane_relpos=new Vec3,particlePlane_projected=new Vec3;Narrowphase.prototype[COLLISION_TYPES.planeParticle]=Narrowphase.prototype.planeParticle;const particleSphere_normal=new Vec3;Narrowphase.prototype[COLLISION_TYPES.sphereParticle]=Narrowphase.prototype.sphereParticle;const cqj=new Quaternion,convexParticle_local=new Vec3,convexParticle_penetratedFaceNormal=new Vec3,convexParticle_vertexToParticle=new Vec3,convexParticle_worldPenetrationVec=new Vec3;Narrowphase.prototype[COLLISION_TYPES.convexParticle]=Narrowphase.prototype.convexParticle,Narrowphase.prototype[COLLISION_TYPES.boxHeightfield]=Narrowphase.prototype.boxHeightfield;const convexHeightfield_tmp1=new Vec3,convexHeightfield_tmp2=new Vec3,convexHeightfield_faceList=[0];Narrowphase.prototype[COLLISION_TYPES.convexHeightfield]=Narrowphase.prototype.convexHeightfield;const sphereHeightfield_tmp1=new Vec3,sphereHeightfield_tmp2=new Vec3;Narrowphase.prototype[COLLISION_TYPES.sphereHeightfield]=Narrowphase.prototype.sphereHeightfield;class OverlapKeeper{constructor(){this.current=[],this.previous=[]}getKey(i,j){if(jcurrent[index];)index++;if(key!==current[index]){for(let j=current.length-1;j>=index;j--)current[j+1]=current[j];current[index]=key}}tick(){const tmp=this.current;this.current=this.previous,this.previous=tmp,this.current.length=0}getDiff(additions,removals){const a=this.current,b=this.previous,al=a.length,bl=b.length;let j=0;for(let i=0;ib[j];)j++;(found=keyA===b[j])||unpackAndPush(additions,keyA)}j=0;for(let i=0;ia[j];)j++;(found=a[j]===keyB)||unpackAndPush(removals,keyB)}}}function unpackAndPush(array,key){array.push((4294901760&key)>>16,65535&key)}class TupleDictionary{constructor(){this.data={keys:[]}}get(i,j){if(i>j){const temp=j;j=i,i=temp}return this.data[i+"-"+j]}set(i,j,value){if(i>j){const temp=j;j=i,i=temp}const key=i+"-"+j;this.get(i,j)||this.data.keys.push(key),this.data[key]=value}reset(){const data=this.data,keys=data.keys;for(;keys.length>0;){delete data[keys.pop()]}}}class World extends EventTarget{constructor(options={}){super(),this.dt=-1,this.allowSleep=!!options.allowSleep,this.contacts=[],this.frictionEquations=[],this.quatNormalizeSkip=void 0!==options.quatNormalizeSkip?options.quatNormalizeSkip:0,this.quatNormalizeFast=void 0!==options.quatNormalizeFast&&options.quatNormalizeFast,this.time=0,this.stepnumber=0,this.default_dt=1/60,this.nextId=0,this.gravity=new Vec3,options.gravity&&this.gravity.copy(options.gravity),this.broadphase=void 0!==options.broadphase?options.broadphase:new NaiveBroadphase,this.bodies=[],this.hasActiveBodies=!1,this.solver=void 0!==options.solver?options.solver:new GSSolver,this.constraints=[],this.narrowphase=new Narrowphase(this),this.collisionMatrix=new ArrayCollisionMatrix,this.collisionMatrixPrevious=new ArrayCollisionMatrix,this.bodyOverlapKeeper=new OverlapKeeper,this.shapeOverlapKeeper=new OverlapKeeper,this.materials=[],this.contactmaterials=[],this.contactMaterialTable=new TupleDictionary,this.defaultMaterial=new Material("default"),this.defaultContactMaterial=new ContactMaterial(this.defaultMaterial,this.defaultMaterial,{friction:.3,restitution:0}),this.doProfiling=!1,this.profile={solve:0,makeContactConstraints:0,broadphase:0,integrate:0,narrowphase:0},this.accumulator=0,this.subsystems=[],this.addBodyEvent={type:"addBody",body:null},this.removeBodyEvent={type:"removeBody",body:null},this.idToBodyMap={},this.broadphase.setWorld(this)}getContactMaterial(m1,m2){return this.contactMaterialTable.get(m1.id,m2.id)}numObjects(){return this.bodies.length}collisionMatrixTick(){const temp=this.collisionMatrixPrevious;this.collisionMatrixPrevious=this.collisionMatrix,this.collisionMatrix=temp,this.collisionMatrix.reset(),this.bodyOverlapKeeper.tick(),this.shapeOverlapKeeper.tick()}addConstraint(c){this.constraints.push(c)}removeConstraint(c){const idx=this.constraints.indexOf(c);-1!==idx&&this.constraints.splice(idx,1)}rayTest(from,to,result){result instanceof RaycastResult?this.raycastClosest(from,to,{skipBackfaces:!0},result):this.raycastAll(from,to,{skipBackfaces:!0},result)}raycastAll(from,to,options={},callback){return options.mode=Ray.ALL,options.from=from,options.to=to,options.callback=callback,tmpRay$1.intersectWorld(this,options)}raycastAny(from,to,options={},result){return options.mode=Ray.ANY,options.from=from,options.to=to,options.result=result,tmpRay$1.intersectWorld(this,options)}raycastClosest(from,to,options={},result){return options.mode=Ray.CLOSEST,options.from=from,options.to=to,options.result=result,tmpRay$1.intersectWorld(this,options)}addBody(body){this.bodies.includes(body)||(body.index=this.bodies.length,this.bodies.push(body),body.world=this,body.initPosition.copy(body.position),body.initVelocity.copy(body.velocity),body.timeLastSleepy=this.time,body instanceof Body&&(body.initAngularVelocity.copy(body.angularVelocity),body.initQuaternion.copy(body.quaternion)),this.collisionMatrix.setNumObjects(this.bodies.length),this.addBodyEvent.body=body,this.idToBodyMap[body.id]=body,this.dispatchEvent(this.addBodyEvent))}removeBody(body){body.world=null;const n=this.bodies.length-1,bodies=this.bodies,idx=bodies.indexOf(body);if(-1!==idx){bodies.splice(idx,1);for(let i=0;i!==bodies.length;i++)bodies[i].index=i;this.collisionMatrix.setNumObjects(n),this.removeBodyEvent.body=body,delete this.idToBodyMap[body.id],this.dispatchEvent(this.removeBodyEvent)}}getBodyById(id){return this.idToBodyMap[id]}getShapeById(id){const bodies=this.bodies;for(let i=0,bl=bodies.length;i=dt&&substeps=0;j-=1)(c.bodyA===p1[j]&&c.bodyB===p2[j]||c.bodyB===p1[j]&&c.bodyA===p2[j])&&(p1.splice(j,1),p2.splice(j,1))}this.collisionMatrixTick(),doProfiling&&(profilingStart=performance.now());const oldcontacts=World_step_oldContacts,NoldContacts=contacts.length;for(i=0;i!==NoldContacts;i++)oldcontacts.push(contacts[i]);contacts.length=0;const NoldFrictionEquations=this.frictionEquations.length;for(i=0;i!==NoldFrictionEquations;i++)frictionEquationPool.push(this.frictionEquations[i]);for(this.frictionEquations.length=0,this.narrowphase.getContacts(p1,p2,this,contacts,oldcontacts,this.frictionEquations,frictionEquationPool),doProfiling&&(profile.narrowphase=performance.now()-profilingStart),doProfiling&&(profilingStart=performance.now()),i=0;i=0&&bj.material.friction>=0&&(mu=bi.material.friction*bj.material.friction),bi.material.restitution>=0&&bj.material.restitution>=0&&(c.restitution=bi.material.restitution*bj.material.restitution)),solver.addEquation(c),bi.allowSleep&&bi.type===Body.DYNAMIC&&bi.sleepState===Body.SLEEPING&&bj.sleepState===Body.AWAKE&&bj.type!==Body.STATIC){bj.velocity.lengthSquared()+bj.angularVelocity.lengthSquared()>=2*bj.sleepSpeedLimit**2&&(bi.wakeUpAfterNarrowphase=!0)}if(bj.allowSleep&&bj.type===Body.DYNAMIC&&bj.sleepState===Body.SLEEPING&&bi.sleepState===Body.AWAKE&&bi.type!==Body.STATIC){bi.velocity.lengthSquared()+bi.angularVelocity.lengthSquared()>=2*bi.sleepSpeedLimit**2&&(bj.wakeUpAfterNarrowphase=!0)}this.collisionMatrix.set(bi,bj,!0),this.collisionMatrixPrevious.get(bi,bj)||(World_step_collideEvent.body=bj,World_step_collideEvent.contact=c,bi.dispatchEvent(World_step_collideEvent),World_step_collideEvent.body=bi,bj.dispatchEvent(World_step_collideEvent)),this.bodyOverlapKeeper.set(bi.id,bj.id),this.shapeOverlapKeeper.set(si.id,sj.id)}for(this.emitContactEvents(),doProfiling&&(profile.makeContactConstraints=performance.now()-profilingStart,profilingStart=performance.now()),i=0;i!==N;i++){const bi=bodies[i];bi.wakeUpAfterNarrowphase&&(bi.wakeUp(),bi.wakeUpAfterNarrowphase=!1)}for(Nconstraints=constraints.length,i=0;i!==Nconstraints;i++){const c=constraints[i];c.update();for(let j=0,Neq=c.equations.length;j!==Neq;j++){const eq=c.equations[j];solver.addEquation(eq)}}solver.solve(dt,this),doProfiling&&(profile.solve=performance.now()-profilingStart),solver.removeAllEquations();const pow=Math.pow;for(i=0;i!==N;i++){const bi=bodies[i];if(bi.type&DYNAMIC){const ld=pow(1-bi.linearDamping,dt),v=bi.velocity;v.scale(ld,v);const av=bi.angularVelocity;if(av){const ad=pow(1-bi.angularDamping,dt);av.scale(ad,av)}}}for(this.dispatchEvent(World_step_preStepEvent),i=0;i!==N;i++){const bi=bodies[i];bi.preStep&&bi.preStep.call(bi)}doProfiling&&(profilingStart=performance.now());const quatNormalize=this.stepnumber%(this.quatNormalizeSkip+1)==0,quatNormalizeFast=this.quatNormalizeFast;for(i=0;i!==N;i++)bodies[i].integrate(dt,quatNormalize,quatNormalizeFast);for(this.clearForces(),this.broadphase.dirty=!0,doProfiling&&(profile.integrate=performance.now()-profilingStart),this.time+=dt,this.stepnumber+=1,this.dispatchEvent(World_step_postStepEvent),i=0;i!==N;i++){const bi=bodies[i],postStep=bi.postStep;postStep&&postStep.call(bi)}let hasActiveBodies=!0;if(this.allowSleep)for(hasActiveBodies=!1,i=0;i!==N;i++){const bi=bodies[i];bi.sleepTick(this.time),bi.sleepState!==Body.SLEEPING&&(hasActiveBodies=!0)}this.hasActiveBodies=hasActiveBodies}clearForces(){const bodies=this.bodies,N=bodies.length;for(let i=0;i!==N;i++){const b=bodies[i];b.force,b.torque;b.force.set(0,0,0),b.torque.set(0,0,0)}}}exports.World=World;new AABB;const tmpRay$1=new Ray;if("undefined"==typeof performance&&(performance={}),!performance.now){let nowOffset=Date.now();performance.timing&&performance.timing.navigationStart&&(nowOffset=performance.timing.navigationStart),performance.now=(()=>Date.now()-nowOffset)}const World_step_postStepEvent={type:"postStep"},World_step_preStepEvent={type:"preStep"},World_step_collideEvent={type:Body.COLLIDE_EVENT_NAME,body:null,contact:null},World_step_oldContacts=[],World_step_frictionEquationPool=[],World_step_p1=[],World_step_p2=[];World.prototype.emitContactEvents=(()=>{const additions=[],removals=[],beginContactEvent={type:"beginContact",bodyA:null,bodyB:null},endContactEvent={type:"endContact",bodyA:null,bodyB:null},beginShapeContactEvent={type:"beginShapeContact",bodyA:null,bodyB:null,shapeA:null,shapeB:null},endShapeContactEvent={type:"endShapeContact",bodyA:null,bodyB:null,shapeA:null,shapeB:null};return function(){const hasBeginContact=this.hasAnyEventListener("beginContact"),hasEndContact=this.hasAnyEventListener("endContact");if((hasBeginContact||hasEndContact)&&this.bodyOverlapKeeper.getDiff(additions,removals),hasBeginContact){for(let i=0,l=additions.length;i{switch(options.cylinderAxis){case"y":return new Ammo.btCylinderShape(btHalfExtents);case"x":return new Ammo.btCylinderShapeX(btHalfExtents);case"z":return new Ammo.btCylinderShapeZ(btHalfExtents)}return null})();return Ammo.destroy(btHalfExtents),_finishCollisionShape(collisionShape,options,_computeScale(matrixWorld,options)),collisionShape};exports.createCylinderShape=createCylinderShape;const createCapsuleShape=function(vertices,matrices,matrixWorld,options={}){options.type=TYPE.CAPSULE,_setOptions(options),options.fit===FIT.ALL&&(options.halfExtents=_computeHalfExtents(_computeBounds(vertices,matrices),options.minHalfExtent,options.maxHalfExtent));const{x:x,y:y,z:z}=options.halfExtents,collisionShape=(()=>{switch(options.cylinderAxis){case"y":return new Ammo.btCapsuleShape(Math.max(x,z),2*y);case"x":return new Ammo.btCapsuleShapeX(Math.max(y,z),2*x);case"z":return new Ammo.btCapsuleShapeZ(Math.max(x,y),2*z)}return null})();return _finishCollisionShape(collisionShape,options,_computeScale(matrixWorld,options)),collisionShape};exports.createCapsuleShape=createCapsuleShape;const createConeShape=function(vertices,matrices,matrixWorld,options={}){options.type=TYPE.CONE,_setOptions(options),options.fit===FIT.ALL&&(options.halfExtents=_computeHalfExtents(_computeBounds(vertices,matrices),options.minHalfExtent,options.maxHalfExtent));const{x:x,y:y,z:z}=options.halfExtents,collisionShape=(()=>{switch(options.cylinderAxis){case"y":return new Ammo.btConeShape(Math.max(x,z),2*y);case"x":return new Ammo.btConeShapeX(Math.max(y,z),2*x);case"z":return new Ammo.btConeShapeZ(Math.max(x,y),2*z)}return null})();return _finishCollisionShape(collisionShape,options,_computeScale(matrixWorld,options)),collisionShape};exports.createConeShape=createConeShape;const createSphereShape=function(vertices,matrices,matrixWorld,options={}){let radius;options.type=TYPE.SPHERE,_setOptions(options),radius=options.fit!==FIT.MANUAL||isNaN(options.sphereRadius)?_computeRadius(vertices,matrices,_computeBounds(vertices,matrices)):options.sphereRadius;const collisionShape=new Ammo.btSphereShape(radius);return _finishCollisionShape(collisionShape,options,_computeScale(matrixWorld,options)),collisionShape};exports.createSphereShape=createSphereShape;const createHullShape=exports.createHullShape=function(){const vertex=new THREE.Vector3,center=new THREE.Vector3,matrix=new THREE.Matrix4;return function(vertices,matrices,matrixWorld,options={}){if(options.type=TYPE.HULL,_setOptions(options),options.fit===FIT.MANUAL)return console.warn("cannot use fit: manual with type: hull"),null;const bounds=_computeBounds(vertices,matrices),btVertex=new Ammo.btVector3,originalHull=new Ammo.btConvexHullShape;originalHull.setMargin(options.margin),center.addVectors(bounds.max,bounds.min).multiplyScalar(.5);let vertexCount=0;for(let i=0;imaxVertices&&console.warn(`too many vertices for hull shape; sampling ~${maxVertices} from ~${vertexCount} vertices`);const p=Math.min(1,maxVertices/vertexCount);for(let i=0;i=100){const shapeHull=new Ammo.btShapeHull(originalHull);shapeHull.buildHull(options.margin),Ammo.destroy(originalHull),collisionShape=new Ammo.btConvexHullShape(Ammo.getPointer(shapeHull.getVertexPointer()),shapeHull.numVertices()),Ammo.destroy(shapeHull)}return Ammo.destroy(btVertex),_finishCollisionShape(collisionShape,options,_computeScale(matrixWorld,options)),collisionShape}}(),createHACDShapes=exports.createHACDShapes=function(){const vector=new THREE.Vector3,center=new THREE.Vector3,matrix=new THREE.Matrix4;return function(vertices,matrices,indexes,matrixWorld,options={}){if(options.type=TYPE.HACD,_setOptions(options),options.fit===FIT.MANUAL)return console.warn("cannot use fit: manual with type: hacd"),[];if(!Ammo.hasOwnProperty("HACD"))return console.warn("HACD unavailable in included build of Ammo.js. Visit https://github.com/mozillareality/ammo.js for the latest version."),[];const bounds=_computeBounds(vertices,matrices),scale=_computeScale(matrixWorld,options);let vertexCount=0,triCount=0;center.addVectors(bounds.max,bounds.min).multiplyScalar(.5);for(let i=0;i{switch(options.heightDataType){case"short":return Ammo.PHY_SHORT;case"float":default:return Ammo.PHY_FLOAT}})(),flipQuadEdges=!options.hasOwnProperty("flipQuadEdges")||options.flipQuadEdges,heightStickLength=heightfieldData.length,heightStickWidth=heightStickLength>0?heightfieldData[0].length:0,data=Ammo._malloc(heightStickLength*heightStickWidth*4),ptr=data/4;let minHeight=Number.POSITIVE_INFINITY,maxHeight=Number.NEGATIVE_INFINITY,index=0;for(let l=0;l{for(let res of collisionShape.resources||[])Ammo.destroy(res);collisionShape.heightfieldData&&Ammo._free(collisionShape.heightfieldData),Ammo.destroy(collisionShape)});const localTransform=new Ammo.btTransform,rotation=new Ammo.btQuaternion;if(localTransform.setIdentity(),localTransform.getOrigin().setValue(options.offset.x,options.offset.y,options.offset.z),rotation.setValue(options.orientation.x,options.orientation.y,options.orientation.z,options.orientation.w),localTransform.setRotation(rotation),Ammo.destroy(rotation),scale){const localScale=new Ammo.btVector3(scale.x,scale.y,scale.z);collisionShape.setLocalScaling(localScale),Ammo.destroy(localScale)}collisionShape.localTransform=localTransform},_computeScale=(exports.iterateGeometries=function(){const inverse=new THREE.Matrix4;return function(root,options,cb){inverse.copy(root.matrixWorld).invert(),(new THREE.Vector3).setFromMatrixScale(root.matrixWorld),root.traverse(mesh=>{const transform=new THREE.Matrix4;mesh.isMesh&&"Sky"!==mesh.name&&(options.includeInvisible||mesh.el&&mesh.el.object3D.visible||mesh.visible)&&(mesh===root?transform.identity():(mesh.updateWorldMatrix(!0),transform.multiplyMatrices(inverse,mesh.matrixWorld)),cb(mesh.geometry.isBufferGeometry?mesh.geometry.attributes.position.array:mesh.geometry.vertices,transform.elements,mesh.geometry.index?mesh.geometry.index.array:null))})}}(),function(){const matrix=new THREE.Matrix4;return function(matrixWorld,options={}){const scale=new THREE.Vector3(1,1,1);return options.fit===FIT.ALL&&(matrix.fromArray(matrixWorld),scale.setFromMatrixScale(matrix)),scale}}()),_computeRadius=function(){const center=new THREE.Vector3;return function(vertices,matrices,bounds){let maxRadiusSq=0,{x:cx,y:cy,z:cz}=bounds.getCenter(center);return _iterateVertices(vertices,matrices,v=>{const dx=cx-v.x,dy=cy-v.y,dz=cz-v.z;maxRadiusSq=Math.max(maxRadiusSq,dx*dx+dy*dy+dz*dz)}),Math.sqrt(maxRadiusSq)}}(),_computeHalfExtents=function(bounds,minHalfExtent,maxHalfExtent){return(new THREE.Vector3).subVectors(bounds.max,bounds.min).multiplyScalar(.5).clampScalar(minHalfExtent,maxHalfExtent)},_computeBounds=function(vertices,matrices){const bounds=new THREE.Box3;let minX=1/0,minY=1/0,minZ=1/0,maxX=-1/0,maxY=-1/0,maxZ=-1/0;return bounds.min.set(0,0,0),bounds.max.set(0,0,0),_iterateVertices(vertices,matrices,v=>{v.xmaxX&&(maxX=v.x),v.y>maxY&&(maxY=v.y),v.z>maxZ&&(maxZ=v.z)}),bounds.min.set(minX,minY,minZ),bounds.max.set(maxX,maxY,maxZ),bounds},_iterateVertices=function(){const vertex=new THREE.Vector3,matrix=new THREE.Matrix4;return function(vertices,matrices,cb){for(let i=0;ithis.tolerance)return!1}return!0},intersectRay:function(ray,target){for(var faces=this.faces,tNear=-1/0,tFar=1/0,i=0,l=faces.length;i0&&vD>=0)return null;var t=0!==vD?-vN/vD:0;if(!(t<=0)&&(vD>0?tFar=Math.min(t,tFar):tNear=Math.max(t,tNear),tNear>tFar))return null}return tNear!==-1/0?ray.at(tNear,target):ray.at(tFar,target),target},intersectsRay:function(ray){return null!==this.intersectRay(ray,v1)},makeEmpty:function(){return this.faces=[],this.vertices=[],this},addVertexToFace:function(vertex,face){return vertex.face=face,null===face.outside?this.assigned.append(vertex):this.assigned.insertBefore(face.outside,vertex),face.outside=vertex,this},removeVertexFromFace:function(vertex,face){return vertex===face.outside&&(null!==vertex.next&&vertex.next.face===face?face.outside=vertex.next:face.outside=null),this.assigned.remove(vertex),this},removeAllVerticesFromFace:function(face){if(null!==face.outside){for(var start=face.outside,end=face.outside;null!==end.next&&end.next.face===face;)end=end.next;return this.assigned.removeSubList(start,end),start.prev=end.next=null,face.outside=null,start}},deleteFaceVertices:function(face,absorbingFace){var faceVertices=this.removeAllVerticesFromFace(face);if(void 0!==faceVertices)if(void 0===absorbingFace)this.unassigned.appendChain(faceVertices);else{var vertex=faceVertices;do{var nextVertex=vertex.next;absorbingFace.distanceToPoint(vertex.point)>this.tolerance?this.addVertexToFace(vertex,absorbingFace):this.unassigned.append(vertex),vertex=nextVertex}while(null!==vertex)}return this},resolveUnassignedPoints:function(newFaces){if(!1===this.unassigned.isEmpty()){var vertex=this.unassigned.first();do{for(var nextVertex=vertex.next,maxDistance=this.tolerance,maxFace=null,i=0;imaxDistance&&(maxDistance=distance,maxFace=face),maxDistance>1e3*this.tolerance)break}}null!==maxFace&&this.addVertexToFace(vertex,maxFace),vertex=nextVertex}while(null!==vertex)}return this},computeExtremes:function(){var i,l,j,min=new _three.Vector3,max=new _three.Vector3,minVertices=[],maxVertices=[];for(i=0;i<3;i++)minVertices[i]=maxVertices[i]=this.vertices[0];for(min.copy(this.vertices[0].point),max.copy(this.vertices[0].point),i=0,l=this.vertices.length;imax.getComponent(j)&&(max.setComponent(j,point.getComponent(j)),maxVertices[j]=vertex)}return this.tolerance=3*Number.EPSILON*(Math.max(Math.abs(min.x),Math.abs(max.x))+Math.max(Math.abs(min.y),Math.abs(max.y))+Math.max(Math.abs(min.z),Math.abs(max.z))),{min:minVertices,max:maxVertices}},computeInitialHull:function(){void 0===line3&&(line3=new _three.Line3,plane=new _three.Plane,closestPoint=new _three.Vector3);var vertex,v0,v1,v2,v3,i,l,j,distance,vertices=this.vertices,extremes=this.computeExtremes(),min=extremes.min,max=extremes.max,maxDistance=0,index=0;for(i=0;i<3;i++)(distance=max[i].point.getComponent(i)-min[i].point.getComponent(i))>maxDistance&&(maxDistance=distance,index=i);for(v0=min[index],v1=max[index],maxDistance=0,line3.set(v0.point,v1.point),i=0,l=this.vertices.length;imaxDistance&&(maxDistance=distance,v2=vertex));for(maxDistance=-1,plane.setFromCoplanarPoints(v0.point,v1.point,v2.point),i=0,l=this.vertices.length;imaxDistance&&(maxDistance=distance,v3=vertex);var faces=[];if(plane.distanceToPoint(v3.point)<0)for(faces.push(Face.create(v0,v1,v2),Face.create(v3,v1,v0),Face.create(v3,v2,v1),Face.create(v3,v0,v2)),i=0;i<3;i++)j=(i+1)%3,faces[i+1].getEdge(2).setTwin(faces[0].getEdge(j)),faces[i+1].getEdge(1).setTwin(faces[j+1].getEdge(0));else for(faces.push(Face.create(v0,v2,v1),Face.create(v3,v0,v1),Face.create(v3,v1,v2),Face.create(v3,v2,v0)),i=0;i<3;i++)j=(i+1)%3,faces[i+1].getEdge(2).setTwin(faces[0].getEdge((3-i)%3)),faces[i+1].getEdge(0).setTwin(faces[j+1].getEdge(1));for(i=0;i<4;i++)this.faces.push(faces[i]);for(i=0,l=vertices.length;imaxDistance&&(maxDistance=distance,maxFace=this.faces[j]);null!==maxFace&&this.addVertexToFace(vertex,maxFace)}return this},reindexFaces:function(){for(var activeFaces=[],i=0;imaxDistance&&(maxDistance=distance,eyeVertex=vertex),vertex=vertex.next}while(null!==vertex&&vertex.face===eyeFace);return eyeVertex}},computeHorizon:function(eyePoint,crossEdge,face,horizon){var edge;this.deleteFaceVertices(face),face.mark=1,edge=null===crossEdge?crossEdge=face.getEdge(0):crossEdge.next;do{var twinEdge=edge.twin,oppositeFace=twinEdge.face;oppositeFace.mark===Visible&&(oppositeFace.distanceToPoint(eyePoint)>this.tolerance?this.computeHorizon(eyePoint,twinEdge,oppositeFace,horizon):horizon.push(edge)),edge=edge.next}while(edge!==crossEdge);return this},addAdjoiningFace:function(eyeVertex,horizonEdge){var face=Face.create(eyeVertex,horizonEdge.tail(),horizonEdge.head());return this.faces.push(face),face.getEdge(-1).setTwin(horizonEdge.twin),face.getEdge(0)},addNewFaces:function(eyeVertex,horizon){this.newFaces=[];for(var firstSideEdge=null,previousSideEdge=null,i=0;i0;)edge=edge.next,i--;for(;i<0;)edge=edge.prev,i++;return edge},compute:function(){void 0===triangle&&(triangle=new _three.Triangle);var a=this.edge.tail(),b=this.edge.head(),c=this.edge.next.head();return triangle.set(a.point,b.point,c.point),triangle.getNormal(this.normal),triangle.getMidpoint(this.midpoint),this.area=triangle.getArea(),this.constant=this.normal.dot(this.midpoint),this},distanceToPoint:function(point){return this.normal.dot(point)-this.constant}}),Object.assign(HalfEdge.prototype,{head:function(){return this.vertex},tail:function(){return this.prev?this.prev.vertex:null},length:function(){var head=this.head(),tail=this.tail();return null!==tail?tail.point.distanceTo(head.point):-1},lengthSquared:function(){var head=this.head(),tail=this.tail();return null!==tail?tail.point.distanceToSquared(head.point):-1},setTwin:function(edge){return this.twin=edge,edge.twin=this,this}}),Object.assign(VertexList.prototype,{first:function(){return this.head},last:function(){return this.tail},clear:function(){return this.head=this.tail=null,this},insertBefore:function(target,vertex){return vertex.prev=target.prev,vertex.next=target,null===vertex.prev?this.head=vertex:vertex.prev.next=vertex,target.prev=vertex,this},insertAfter:function(target,vertex){return vertex.prev=target,vertex.next=target.next,null===vertex.next?this.tail=vertex:vertex.next.prev=vertex,target.next=vertex,this},append:function(vertex){return null===this.head?this.head=vertex:this.tail.next=vertex,vertex.prev=this.tail,vertex.next=null,this.tail=vertex,this},appendChain:function(vertex){for(null===this.head?this.head=vertex:this.tail.next=vertex,vertex.prev=this.tail;null!==vertex.next;)vertex=vertex.next;return this.tail=vertex,this},remove:function(vertex){return null===vertex.prev?this.head=vertex.next:vertex.prev.next=vertex.next,null===vertex.next?this.tail=vertex.prev:vertex.next.prev=vertex.prev,this},removeSubList:function(a,b){return null===a.prev?this.head=b.next:a.prev.next=b.next,null===b.next?this.tail=a.prev:b.next.prev=a.prev,this},isEmpty:function(){return null===this.head}}),ConvexHull}();const _v1=new _three.Vector3,_v2=new _three.Vector3,_q1=new _three.Quaternion;function getGeometry(object){const meshes=function(object){const meshes=[];return object.traverse(function(o){o.isMesh&&meshes.push(o)}),meshes}(object);if(0===meshes.length)return null;if(1===meshes.length)return normalizeGeometry(meshes[0]);let mesh;const geometries=[];for(;mesh=meshes.pop();)geometries.push(simplifyGeometry(normalizeGeometry(mesh)));return function(geometries){let vertexCount=0;for(let i=0;i{this.loadedEventFired=!0},{once:!0}),this.system.initialized&&this.loadedEventFired&&this.initBody()},initBody:function(){const pos=new THREE.Vector3,quat=new THREE.Quaternion;new THREE.Box3;return function(){const el=this.el,data=this.data;this.localScaling=new Ammo.btVector3;const obj=this.el.object3D;obj.getWorldPosition(pos),obj.getWorldQuaternion(quat),this.prevScale=new THREE.Vector3(1,1,1),this.prevNumChildShapes=0,this.msTransform=new Ammo.btTransform,this.msTransform.setIdentity(),this.rotation=new Ammo.btQuaternion(quat.x,quat.y,quat.z,quat.w),this.msTransform.getOrigin().setValue(pos.x,pos.y,pos.z),this.msTransform.setRotation(this.rotation),this.motionState=new Ammo.btDefaultMotionState(this.msTransform),this.localInertia=new Ammo.btVector3(0,0,0),this.compoundShape=new Ammo.btCompoundShape(!0),this.rbInfo=new Ammo.btRigidBodyConstructionInfo(data.mass,this.motionState,this.compoundShape,this.localInertia),this.rbInfo.m_restitution=((num,min,max)=>Math.min(Math.max(num,min),max))(this.data.restitution,0,1),this.body=new Ammo.btRigidBody(this.rbInfo),this.body.setActivationState(ACTIVATION_STATES.indexOf(data.activationState)+1),this.body.setSleepingThresholds(data.linearSleepingThreshold,data.angularSleepingThreshold),this.body.setDamping(data.linearDamping,data.angularDamping);const angularFactor=new Ammo.btVector3(data.angularFactor.x,data.angularFactor.y,data.angularFactor.z);this.body.setAngularFactor(angularFactor),Ammo.destroy(angularFactor),this._updateBodyGravity(data.gravity),this.updateCollisionFlags(),this.el.body=this.body,this.body.el=el,this.isLoaded=!0,this.el.emit("body-loaded",{body:this.el.body}),this._addToSystem()}}(),tick:function(){this.system.initialized&&!this.isLoaded&&this.loadedEventFired&&this.initBody()},_updateBodyGravity(gravity){if(void 0!==gravity.x&&void 0!==gravity.y&&void 0!==gravity.z){const gravityBtVec=new Ammo.btVector3(gravity.x,gravity.y,gravity.z);epsilon=.001,u=gravityBtVec,v=this.system.driver.physicsWorld.getGravity(),Math.abs(u.x()-v.x()){vertices.push(vertexArray),matrices.push(matrixArray),indexes.push(indexArray)});const collisionShapes=threeToAmmo.createCollisionShapes(vertices,matrices,indexes,matrixWorld.elements,data);shapeComponent.addShapes(collisionShapes)},play:function(){this.isLoaded&&this._addToSystem()},_addToSystem:function(){this.addedToSystem||(this.system.addBody(this.body,this.data.collisionFilterGroup,this.data.collisionFilterMask),this.data.emitCollisionEvents&&this.system.driver.addEventListener(this.body),this.system.addComponent(this),this.addedToSystem=!0)},pause:function(){this.addedToSystem&&(this.system.removeComponent(this),this.system.removeBody(this.body),this.addedToSystem=!1)},update:function(prevData){if(this.isLoaded){if(!this.hasUpdated)return void(this.hasUpdated=!0);const data=this.data;if(prevData.type===data.type&&prevData.disableCollision===data.disableCollision||this.updateCollisionFlags(),prevData.activationState!==data.activationState&&(this.body.forceActivationState(ACTIVATION_STATES.indexOf(data.activationState)+1),data.activationState===ACTIVATION_STATE.ACTIVE_TAG&&this.body.activate(!0)),prevData.collisionFilterGroup!==data.collisionFilterGroup||prevData.collisionFilterMask!==data.collisionFilterMask){const broadphaseProxy=this.body.getBroadphaseProxy();broadphaseProxy.set_m_collisionFilterGroup(data.collisionFilterGroup),broadphaseProxy.set_m_collisionFilterMask(data.collisionFilterMask),this.system.driver.broadphase.getOverlappingPairCache().removeOverlappingPairsContainingProxy(broadphaseProxy,this.system.driver.dispatcher)}if(prevData.linearDamping==data.linearDamping&&prevData.angularDamping==data.angularDamping||this.body.setDamping(data.linearDamping,data.angularDamping),almostEqualsVector3(.001,prevData.gravity,data.gravity)||this._updateBodyGravity(data.gravity),prevData.linearSleepingThreshold==data.linearSleepingThreshold&&prevData.angularSleepingThreshold==data.angularSleepingThreshold||this.body.setSleepingThresholds(data.linearSleepingThreshold,data.angularSleepingThreshold),!almostEqualsVector3(.001,prevData.angularFactor,data.angularFactor)){const angularFactor=new Ammo.btVector3(data.angularFactor.x,data.angularFactor.y,data.angularFactor.z);this.body.setAngularFactor(angularFactor),Ammo.destroy(angularFactor)}prevData.restitution!=data.restitution&&console.warn("ammo-body restitution cannot be updated from its initial value.")}},remove:function(){this.triMesh&&Ammo.destroy(this.triMesh),this.localScaling&&Ammo.destroy(this.localScaling),this.compoundShape&&Ammo.destroy(this.compoundShape),Ammo.destroy(this.rbInfo),Ammo.destroy(this.msTransform),Ammo.destroy(this.motionState),Ammo.destroy(this.localInertia),Ammo.destroy(this.rotation),this.body&&(this.data.emitCollisionEvents||Ammo.destroy(this.body),delete this.body)},beforeStep:function(){this._updateShapes(),this.data.type===TYPE.KINEMATIC&&this.syncToPhysics()},step:function(){this.data.type===TYPE.DYNAMIC&&this.syncFromPhysics()},syncToPhysics:function(){const q=new THREE.Quaternion,v=new THREE.Vector3,q2=new THREE.Vector3,v2=new THREE.Vector3;return function(){const el=this.el,parentEl=el.parentEl;if(!this.body)return;this.motionState.getWorldTransform(this.msTransform),parentEl.isScene?(v.copy(el.object3D.position),q.copy(el.object3D.quaternion)):(el.object3D.getWorldPosition(v),el.object3D.getWorldQuaternion(q));const position=this.msTransform.getOrigin();v2.set(position.x(),position.y(),position.z());const quaternion=this.msTransform.getRotation();q2.set(quaternion.x(),quaternion.y(),quaternion.z(),quaternion.w()),almostEqualsVector3(.001,v,v2)&&function(epsilon,u,v){return Math.abs(u.x-v.x)0;){const collisionShape=this.collisionShapes.pop();collisionShape.destroy(),Ammo.destroy(collisionShape.localTransform)}}};module.exports.definition=AmmoShape,module.exports.Component=AFRAME.registerComponent("ammo-shape",AmmoShape)},{"../../constants":20,"three-to-ammo":6}],18:[function(require,module,exports){var CANNON=require("cannon-es"),Shape={schema:{shape:{default:"box",oneOf:["box","sphere","cylinder"]},offset:{type:"vec3",default:{x:0,y:0,z:0}},orientation:{type:"vec4",default:{x:0,y:0,z:0,w:1}},radius:{type:"number",default:1,if:{shape:["sphere"]}},halfExtents:{type:"vec3",default:{x:.5,y:.5,z:.5},if:{shape:["box"]}},radiusTop:{type:"number",default:1,if:{shape:["cylinder"]}},radiusBottom:{type:"number",default:1,if:{shape:["cylinder"]}},height:{type:"number",default:1,if:{shape:["cylinder"]}},numSegments:{type:"int",default:8,if:{shape:["cylinder"]}}},multiple:!0,init:function(){this.el.sceneEl.hasLoaded?this.initShape():this.el.sceneEl.addEventListener("loaded",this.initShape.bind(this))},initShape:function(){this.bodyEl=this.el;for(var bodyType=this._findType(this.bodyEl),data=this.data;!bodyType&&this.bodyEl.parentNode!=this.el.sceneEl;)this.bodyEl=this.bodyEl.parentNode,bodyType=this._findType(this.bodyEl);if(bodyType){var shape,offset,orientation,scale=new THREE.Vector3;switch(this.bodyEl.object3D.getWorldScale(scale),data.hasOwnProperty("offset")&&(offset=new CANNON.Vec3(data.offset.x*scale.x,data.offset.y*scale.y,data.offset.z*scale.z)),data.hasOwnProperty("orientation")&&(orientation=new CANNON.Quaternion).copy(data.orientation),data.shape){case"sphere":shape=new CANNON.Sphere(data.radius*scale.x);break;case"box":var halfExtents=new CANNON.Vec3(data.halfExtents.x*scale.x,data.halfExtents.y*scale.y,data.halfExtents.z*scale.z);shape=new CANNON.Box(halfExtents);break;case"cylinder":shape=new CANNON.Cylinder(data.radiusTop*scale.x,data.radiusBottom*scale.x,data.height*scale.y,data.numSegments);var quat=new CANNON.Quaternion;quat.setFromEuler(90*THREE.MathUtils.DEG2RAD,0,0,"XYZ").normalize(),orientation.mult(quat,orientation);break;default:return void console.warn(data.shape+" shape not supported")}this.bodyEl.body?this.bodyEl.components[bodyType].addShape(shape,offset,orientation):this.bodyEl.addEventListener("body-loaded",function(){this.bodyEl.components[bodyType].addShape(shape,offset,orientation)},{once:!0})}else console.warn("body not found")},_findType:function(el){return el.hasAttribute("body")?"body":el.hasAttribute("dynamic-body")?"dynamic-body":el.hasAttribute("static-body")?"static-body":null},remove:function(){this.bodyEl.parentNode&&console.warn("removing shape component not currently supported")}};module.exports.definition=Shape,module.exports.Component=AFRAME.registerComponent("shape",Shape)},{"cannon-es":5}],19:[function(require,module,exports){var CANNON=require("cannon-es");module.exports=AFRAME.registerComponent("spring",{multiple:!0,schema:{target:{type:"selector"},restLength:{default:1,min:0},stiffness:{default:100,min:0},damping:{default:1,min:0},localAnchorA:{type:"vec3",default:{x:0,y:0,z:0}},localAnchorB:{type:"vec3",default:{x:0,y:0,z:0}}},init:function(){this.system=this.el.sceneEl.systems.physics,this.system.addComponent(this),this.isActive=!0,this.spring=null},update:function(oldData){var el=this.el,data=this.data;data.target?el.body&&data.target.body?(this.createSpring(),this.updateSpring(oldData)):(el.body?data.target:el).addEventListener("body-loaded",this.update.bind(this,{})):console.warn("Spring: invalid target specified.")},updateSpring:function(oldData){if(this.spring){var data=this.data,spring=this.spring;Object.keys(data).forEach(function(attr){if(data[attr]!==oldData[attr]){if("target"===attr)return void(spring.bodyB=data.target.body);spring[attr]=data[attr]}})}else console.warn("Spring: Component attempted to change spring before its created. No changes made.")},createSpring:function(){this.spring||(this.spring=new CANNON.Spring(this.el.body))},step:function(t,dt){return this.spring&&this.isActive?this.spring.applyForce():void 0},play:function(){this.isActive=!0},pause:function(){this.isActive=!1},remove:function(){this.spring&&delete this.spring,this.spring=null}})},{"cannon-es":5}],20:[function(require,module,exports){module.exports={GRAVITY:-9.8,MAX_INTERVAL:4/60,ITERATIONS:10,CONTACT_MATERIAL:{friction:.01,restitution:.3,contactEquationStiffness:1e8,contactEquationRelaxation:3,frictionEquationStiffness:1e8,frictionEquationRegularization:3},ACTIVATION_STATE:{ACTIVE_TAG:"active",ISLAND_SLEEPING:"islandSleeping",WANTS_DEACTIVATION:"wantsDeactivation",DISABLE_DEACTIVATION:"disableDeactivation",DISABLE_SIMULATION:"disableSimulation"},COLLISION_FLAG:{STATIC_OBJECT:1,KINEMATIC_OBJECT:2,NO_CONTACT_RESPONSE:4,CUSTOM_MATERIAL_CALLBACK:8,CHARACTER_OBJECT:16,DISABLE_VISUALIZE_OBJECT:32,DISABLE_SPU_COLLISION_PROCESSING:64},TYPE:{STATIC:"static",DYNAMIC:"dynamic",KINEMATIC:"kinematic"},SHAPE:{BOX:"box",CYLINDER:"cylinder",SPHERE:"sphere",CAPSULE:"capsule",CONE:"cone",HULL:"hull",HACD:"hacd",VHACD:"vhacd",MESH:"mesh",HEIGHTFIELD:"heightfield"},FIT:{ALL:"all",MANUAL:"manual"},CONSTRAINT:{LOCK:"lock",FIXED:"fixed",SPRING:"spring",SLIDER:"slider",HINGE:"hinge",CONE_TWIST:"coneTwist",POINT_TO_POINT:"pointToPoint"}}},{}],21:[function(require,module,exports){const Driver=require("./driver");"undefined"!=typeof window&&(window.AmmoModule=window.Ammo,window.Ammo=null);function AmmoDriver(){this.collisionConfiguration=null,this.dispatcher=null,this.broadphase=null,this.solver=null,this.physicsWorld=null,this.debugDrawer=null,this.els=new Map,this.eventListeners=[],this.collisions=new Map,this.collisionKeys=[],this.currentCollisions=new Map}AmmoDriver.prototype=new Driver,AmmoDriver.prototype.constructor=AmmoDriver,module.exports=AmmoDriver,AmmoDriver.prototype.init=function(worldConfig){return new Promise(resolve=>{AmmoModule().then(result=>{Ammo=result,this.epsilon=worldConfig.epsilon||1e-5,this.debugDrawMode=worldConfig.debugDrawMode||THREE.AmmoDebugConstants.NoDebug,this.maxSubSteps=worldConfig.maxSubSteps||4,this.fixedTimeStep=worldConfig.fixedTimeStep||1/60,this.collisionConfiguration=new Ammo.btDefaultCollisionConfiguration,this.dispatcher=new Ammo.btCollisionDispatcher(this.collisionConfiguration),this.broadphase=new Ammo.btDbvtBroadphase,this.solver=new Ammo.btSequentialImpulseConstraintSolver,this.physicsWorld=new Ammo.btDiscreteDynamicsWorld(this.dispatcher,this.broadphase,this.solver,this.collisionConfiguration),this.physicsWorld.setForceUpdateAllAabbs(!1),this.physicsWorld.setGravity(new Ammo.btVector3(0,worldConfig.hasOwnProperty("gravity")?worldConfig.gravity:-9.8,0)),this.physicsWorld.getSolverInfo().set_m_numIterations(worldConfig.solverIterations),resolve()})})},AmmoDriver.prototype.addBody=function(body,group,mask){this.physicsWorld.addRigidBody(body,group,mask),this.els.set(Ammo.getPointer(body),body.el)},AmmoDriver.prototype.removeBody=function(body){this.physicsWorld.removeRigidBody(body),this.removeEventListener(body);const bodyptr=Ammo.getPointer(body);this.els.delete(bodyptr),this.collisions.delete(bodyptr),this.collisionKeys.splice(this.collisionKeys.indexOf(bodyptr),1),this.currentCollisions.delete(bodyptr)},AmmoDriver.prototype.updateBody=function(body){this.els.has(Ammo.getPointer(body))&&this.physicsWorld.updateSingleAabb(body)},AmmoDriver.prototype.step=function(deltaTime){this.physicsWorld.stepSimulation(deltaTime,this.maxSubSteps,this.fixedTimeStep);const numManifolds=this.dispatcher.getNumManifolds();for(let i=0;i=0;j--){const body1ptr=body1ptrs[j];this.currentCollisions.get(body0ptr).has(body1ptr)||(-1!==this.eventListeners.indexOf(body0ptr)&&this.els.get(body0ptr).emit("collideend",{targetEl:this.els.get(body1ptr)}),-1!==this.eventListeners.indexOf(body1ptr)&&this.els.get(body1ptr).emit("collideend",{targetEl:this.els.get(body0ptr)}),body1ptrs.splice(j,1))}this.currentCollisions.get(body0ptr).clear()}this.debugDrawer&&this.debugDrawer.update()},AmmoDriver.prototype.addConstraint=function(constraint){this.physicsWorld.addConstraint(constraint,!1)},AmmoDriver.prototype.removeConstraint=function(constraint){this.physicsWorld.removeConstraint(constraint)},AmmoDriver.prototype.addEventListener=function(body){this.eventListeners.push(Ammo.getPointer(body))},AmmoDriver.prototype.removeEventListener=function(body){const ptr=Ammo.getPointer(body);-1!==this.eventListeners.indexOf(ptr)&&this.eventListeners.splice(this.eventListeners.indexOf(ptr),1)},AmmoDriver.prototype.destroy=function(){Ammo.destroy(this.collisionConfiguration),Ammo.destroy(this.dispatcher),Ammo.destroy(this.broadphase),Ammo.destroy(this.solver),Ammo.destroy(this.physicsWorld),Ammo.destroy(this.debugDrawer)},AmmoDriver.prototype.getDebugDrawer=function(scene,options){return this.debugDrawer||((options=options||{}).debugDrawMode=options.debugDrawMode||this.debugDrawMode,this.debugDrawer=new THREE.AmmoDebugDrawer(scene,this.physicsWorld,options)),this.debugDrawer}},{"./driver":22}],22:[function(require,module,exports){function Driver(){}function abstractMethod(){throw new Error("Method not implemented.")}module.exports=Driver,Driver.prototype.init=abstractMethod,Driver.prototype.step=abstractMethod,Driver.prototype.destroy=abstractMethod,Driver.prototype.addBody=abstractMethod,Driver.prototype.removeBody=abstractMethod,Driver.prototype.applyBodyMethod=abstractMethod,Driver.prototype.updateBodyProperties=abstractMethod,Driver.prototype.addMaterial=abstractMethod,Driver.prototype.addContactMaterial=abstractMethod,Driver.prototype.addConstraint=abstractMethod,Driver.prototype.removeConstraint=abstractMethod,Driver.prototype.getContacts=abstractMethod},{}],23:[function(require,module,exports){module.exports={INIT:"init",STEP:"step",ADD_BODY:"add-body",REMOVE_BODY:"remove-body",APPLY_BODY_METHOD:"apply-body-method",UPDATE_BODY_PROPERTIES:"update-body-properties",ADD_MATERIAL:"add-material",ADD_CONTACT_MATERIAL:"add-contact-material",ADD_CONSTRAINT:"add-constraint",REMOVE_CONSTRAINT:"remove-constraint",COLLIDE:"collide"}},{}],24:[function(require,module,exports){var CANNON=require("cannon-es"),Driver=require("./driver");function LocalDriver(){this.world=null,this.materials={},this.contactMaterial=null}LocalDriver.prototype=new Driver,LocalDriver.prototype.constructor=LocalDriver,module.exports=LocalDriver,LocalDriver.prototype.init=function(worldConfig){var world=new CANNON.World;world.quatNormalizeSkip=worldConfig.quatNormalizeSkip,world.quatNormalizeFast=worldConfig.quatNormalizeFast,world.solver.iterations=worldConfig.solverIterations,world.gravity.set(0,worldConfig.gravity,0),world.broadphase=new CANNON.NaiveBroadphase,this.world=world},LocalDriver.prototype.step=function(deltaMS){this.world.step(deltaMS)},LocalDriver.prototype.destroy=function(){delete this.world,delete this.contactMaterial,this.materials={}},LocalDriver.prototype.addBody=function(body){this.world.addBody(body)},LocalDriver.prototype.removeBody=function(body){this.world.removeBody(body)},LocalDriver.prototype.applyBodyMethod=function(body,methodName,args){body["__"+methodName].apply(body,args)},LocalDriver.prototype.updateBodyProperties=function(){},LocalDriver.prototype.getMaterial=function(name){return this.materials[name]},LocalDriver.prototype.addMaterial=function(materialConfig){this.materials[materialConfig.name]=new CANNON.Material(materialConfig),this.materials[materialConfig.name].name=materialConfig.name},LocalDriver.prototype.addContactMaterial=function(matName1,matName2,contactMaterialConfig){var mat1=this.materials[matName1],mat2=this.materials[matName2];this.contactMaterial=new CANNON.ContactMaterial(mat1,mat2,contactMaterialConfig),this.world.addContactMaterial(this.contactMaterial)},LocalDriver.prototype.addConstraint=function(constraint){constraint.type||(constraint instanceof CANNON.LockConstraint?constraint.type="LockConstraint":constraint instanceof CANNON.DistanceConstraint?constraint.type="DistanceConstraint":constraint instanceof CANNON.HingeConstraint?constraint.type="HingeConstraint":constraint instanceof CANNON.ConeTwistConstraint?constraint.type="ConeTwistConstraint":constraint instanceof CANNON.PointToPointConstraint&&(constraint.type="PointToPointConstraint")),this.world.addConstraint(constraint)},LocalDriver.prototype.removeConstraint=function(constraint){this.world.removeConstraint(constraint)},LocalDriver.prototype.getContacts=function(){return this.world.contacts}},{"./driver":22,"cannon-es":5}],25:[function(require,module,exports){var Driver=require("./driver");function NetworkDriver(){throw new Error("[NetworkDriver] Driver not implemented.")}NetworkDriver.prototype=new Driver,NetworkDriver.prototype.constructor=NetworkDriver,module.exports=NetworkDriver},{"./driver":22}],26:[function(require,module,exports){function EventTarget(){this.listeners=[]}module.exports=function(worker){var targetA=new EventTarget,targetB=new EventTarget;return targetA.setTarget(targetB),targetB.setTarget(targetA),worker(targetA),targetB},EventTarget.prototype.setTarget=function(target){this.target=target},EventTarget.prototype.addEventListener=function(type,fn){this.listeners.push(fn)},EventTarget.prototype.dispatchEvent=function(type,event){for(var i=0;ithis.frameDelay;)this.frameBuffer.shift(),prevFrame=this.frameBuffer[0],nextFrame=this.frameBuffer[1];if(prevFrame&&nextFrame){var mix=(timestamp-prevFrame.timestamp)/this.frameDelay;for(var id in mix=(mix-(1-1/this.interpBufferSize))*this.interpBufferSize,prevFrame.bodies)prevFrame.bodies.hasOwnProperty(id)&&nextFrame.bodies.hasOwnProperty(id)&&protocol.deserializeInterpBodyUpdate(prevFrame.bodies[id],nextFrame.bodies[id],this.bodies[id],mix)}}},WorkerDriver.prototype.destroy=function(){this.worker.terminate(),delete this.worker},WorkerDriver.prototype._onMessage=function(event){if(event.data.type===Event.STEP){var bodies=event.data.bodies;if(this.contacts=event.data.contacts,this.interpolate)this.frameBuffer.push({timestamp:performance.now(),bodies:bodies});else for(var id in bodies)bodies.hasOwnProperty(id)&&protocol.deserializeBodyUpdate(bodies[id],this.bodies[id])}else{if(event.data.type!==Event.COLLIDE)throw new Error("[WorkerDriver] Unexpected message type.");var body=this.bodies[event.data.bodyID],target=this.bodies[event.data.targetID],contact=protocol.deserializeContact(event.data.contact,this.bodies);if(!body._listeners||!body._listeners.collide)return;for(var i=0;ithis.countBodiesAmmo(),local:()=>this.countBodiesCannon(!1),worker:()=>this.countBodiesCannon(!0)},this.bodyTypeToStatsPropertyMap={ammo:{[TYPE.STATIC]:"staticBodies",[TYPE.KINEMATIC]:"kinematicBodies",[TYPE.DYNAMIC]:"dynamicBodies"},cannon:{[CANNON.Body.STATIC]:"staticBodies",[CANNON.Body.DYNAMIC]:"dynamicBodies"}},this.el.sceneEl.setAttribute("stats-collector","inEvent: physics-tick-data;\n properties: before, after, engine, total;\n outputFrequency: 100;\n outEvent: physics-tick-summary;\n outputs: percentile__50, percentile__90, max")}if(this.statsToPanel){const scene=this.el.sceneEl,space="   ";scene.setAttribute("stats-panel",""),scene.setAttribute("stats-group__bodies","label: Physics Bodies"),scene.setAttribute("stats-row__b1","group: bodies;\n event:physics-body-data;\n properties: staticBodies;\n label: Static"),scene.setAttribute("stats-row__b2","group: bodies;\n event:physics-body-data;\n properties: dynamicBodies;\n label: Dynamic"),"local"===this.data.driver||"worker"===this.data.driver?scene.setAttribute("stats-row__b3","group: bodies;\n event:physics-body-data;\n properties: contacts;\n label: Contacts"):"ammo"===this.data.driver&&(scene.setAttribute("stats-row__b3","group: bodies;\n event:physics-body-data;\n properties: kinematicBodies;\n label: Kinematic"),scene.setAttribute("stats-row__b4","group: bodies;\n event: physics-body-data;\n properties: manifolds;\n label: Manifolds"),scene.setAttribute("stats-row__b5","group: bodies;\n event: physics-body-data;\n properties: manifoldContacts;\n label: Contacts"),scene.setAttribute("stats-row__b6","group: bodies;\n event: physics-body-data;\n properties: collisions;\n label: Collisions"),scene.setAttribute("stats-row__b7","group: bodies;\n event: physics-body-data;\n properties: collisionKeys;\n label: Coll Keys")),scene.setAttribute("stats-group__tick",`label: Physics Ticks: Median${space}90th%${space}99th%`),scene.setAttribute("stats-row__1","group: tick;\n event:physics-tick-summary;\n properties: before.percentile__50, \n before.percentile__90, \n before.max;\n label: Before"),scene.setAttribute("stats-row__2","group: tick;\n event:physics-tick-summary;\n properties: after.percentile__50, \n after.percentile__90, \n after.max; \n label: After"),scene.setAttribute("stats-row__3","group: tick; \n event:physics-tick-summary; \n properties: engine.percentile__50, \n engine.percentile__90, \n engine.max;\n label: Engine"),scene.setAttribute("stats-row__4","group: tick;\n event:physics-tick-summary;\n properties: total.percentile__50, \n total.percentile__90, \n total.max;\n label: Total")}},tick:function(t,dt){if(!this.initialized||!dt)return;const beforeStartTime=performance.now();var i,callbacks=this.callbacks;for(i=0;i{const property=this.bodyTypeToStatsPropertyMap.ammo[(el=el,el.components["ammo-body"].data.type)];statsData[property]++})},countBodiesCannon(worker){const statsData=this.statsBodyData;statsData.contacts=worker?this.driver.contacts.length:this.driver.world.contacts.length,statsData.staticBodies=0,statsData.dynamicBodies=0,(worker?Object.values(this.driver.bodies):this.driver.world.bodies).forEach(body=>{const property=this.bodyTypeToStatsPropertyMap.cannon[body.type];statsData[property]++})},setDebug:function(debug){this.debug=debug,"ammo"===this.data.driver&&this.initialized&&(debug&&!this.debugDrawer?(this.debugDrawer=this.driver.getDebugDrawer(this.el.object3D),this.debugDrawer.enable()):this.debugDrawer&&(this.debugDrawer.disable(),this.debugDrawer=null))},addBody:function(body,group,mask){var driver=this.driver;"local"===this.data.driver&&(body.__applyImpulse=body.applyImpulse,body.applyImpulse=function(){driver.applyBodyMethod(body,"applyImpulse",arguments)},body.__applyForce=body.applyForce,body.applyForce=function(){driver.applyBodyMethod(body,"applyForce",arguments)},body.updateProperties=function(){driver.updateBodyProperties(body)},this.listeners[body.id]=function(e){body.el.emit("collide",e)},body.addEventListener("collide",this.listeners[body.id])),this.driver.addBody(body,group,mask)},removeBody:function(body){this.driver.removeBody(body),"local"!==this.data.driver&&"worker"!==this.data.driver||(body.removeEventListener("collide",this.listeners[body.id]),delete this.listeners[body.id],body.applyImpulse=body.__applyImpulse,delete body.__applyImpulse,body.applyForce=body.__applyForce,delete body.__applyForce,delete body.updateProperties)},addConstraint:function(constraint){this.driver.addConstraint(constraint)},removeConstraint:function(constraint){this.driver.removeConstraint(constraint)},addComponent:function(component){var callbacks=this.callbacks;component.beforeStep&&callbacks.beforeStep.push(component),component.step&&callbacks.step.push(component),component.afterStep&&callbacks.afterStep.push(component)},removeComponent:function(component){var callbacks=this.callbacks;component.beforeStep&&callbacks.beforeStep.splice(callbacks.beforeStep.indexOf(component),1),component.step&&callbacks.step.splice(callbacks.step.indexOf(component),1),component.afterStep&&callbacks.afterStep.splice(callbacks.afterStep.indexOf(component),1)},getContacts:function(){return this.driver.getContacts()},getMaterial:function(name){return this.driver.getMaterial(name)}})},{"./constants":20,"./drivers/ammo-driver":21,"./drivers/local-driver":24,"./drivers/network-driver":25,"./drivers/worker-driver":27,"aframe-stats-panel":3,"cannon-es":5}],30:[function(require,module,exports){module.exports.slerp=function(a,b,t){if(t<=0)return a;if(t>=1)return b;var x=a[0],y=a[1],z=a[2],w=a[3],cosHalfTheta=w*b[3]+x*b[0]+y*b[1]+z*b[2];if(!(cosHalfTheta<0))return b;if((a=a.slice())[3]=-b[3],a[0]=-b[0],a[1]=-b[1],a[2]=-b[2],(cosHalfTheta=-cosHalfTheta)>=1)return a[3]=w,a[0]=x,a[1]=y,a[2]=z,this;var sinHalfTheta=Math.sqrt(1-cosHalfTheta*cosHalfTheta);if(Math.abs(sinHalfTheta)<.001)return a[3]=.5*(w+a[3]),a[0]=.5*(x+a[0]),a[1]=.5*(y+a[1]),a[2]=.5*(z+a[2]),this;var halfTheta=Math.atan2(sinHalfTheta,cosHalfTheta),ratioA=Math.sin((1-t)*halfTheta)/sinHalfTheta,ratioB=Math.sin(t*halfTheta)/sinHalfTheta;return a[3]=w*ratioA+a[3]*ratioB,a[0]=x*ratioA+a[0]*ratioB,a[1]=y*ratioA+a[1]*ratioB,a[2]=z*ratioA+a[2]*ratioB,a}},{}],31:[function(require,module,exports){var CANNON=require("cannon-es"),mathUtils=require("./math"),ID="__id";module.exports.ID=ID;var nextID={};function serializeShape(shape){var shapeMsg={type:shape.type};if(shape.type===CANNON.Shape.types.BOX)shapeMsg.halfExtents=serializeVec3(shape.halfExtents);else if(shape.type===CANNON.Shape.types.SPHERE)shapeMsg.radius=shape.radius;else{if(shape._type!==CANNON.Shape.types.CYLINDER)throw new Error("Unimplemented shape type: %s",shape.type);shapeMsg.type=CANNON.Shape.types.CYLINDER,shapeMsg.radiusTop=shape.radiusTop,shapeMsg.radiusBottom=shape.radiusBottom,shapeMsg.height=shape.height,shapeMsg.numSegments=shape.numSegments}return shapeMsg}function deserializeShape(message){var shape;if(message.type===CANNON.Shape.types.BOX)shape=new CANNON.Box(deserializeVec3(message.halfExtents));else if(message.type===CANNON.Shape.types.SPHERE)shape=new CANNON.Sphere(message.radius);else{if(message.type!==CANNON.Shape.types.CYLINDER)throw new Error("Unimplemented shape type: %s",message.type);(shape=new CANNON.Cylinder(message.radiusTop,message.radiusBottom,message.height,message.numSegments))._type=CANNON.Shape.types.CYLINDER}return shape}function serializeVec3(vec3){return vec3.toArray()}function deserializeVec3(message){return new CANNON.Vec3(message[0],message[1],message[2])}function serializeQuaternion(quat){return quat.toArray()}function deserializeQuaternion(message){return new CANNON.Quaternion(message[0],message[1],message[2],message[3])}module.exports.assignID=function(prefix,object){object[ID]||(nextID[prefix]=nextID[prefix]||1,object[ID]=prefix+"_"+nextID[prefix]++)},module.exports.serializeBody=function(body){return{shapes:body.shapes.map(serializeShape),shapeOffsets:body.shapeOffsets.map(serializeVec3),shapeOrientations:body.shapeOrientations.map(serializeQuaternion),position:serializeVec3(body.position),quaternion:body.quaternion.toArray(),velocity:serializeVec3(body.velocity),angularVelocity:serializeVec3(body.angularVelocity),id:body[ID],mass:body.mass,linearDamping:body.linearDamping,angularDamping:body.angularDamping,fixedRotation:body.fixedRotation,allowSleep:body.allowSleep,sleepSpeedLimit:body.sleepSpeedLimit,sleepTimeLimit:body.sleepTimeLimit}},module.exports.deserializeBodyUpdate=function(message,body){return body.position.set(message.position[0],message.position[1],message.position[2]),body.quaternion.set(message.quaternion[0],message.quaternion[1],message.quaternion[2],message.quaternion[3]),body.velocity.set(message.velocity[0],message.velocity[1],message.velocity[2]),body.angularVelocity.set(message.angularVelocity[0],message.angularVelocity[1],message.angularVelocity[2]),body.linearDamping=message.linearDamping,body.angularDamping=message.angularDamping,body.fixedRotation=message.fixedRotation,body.allowSleep=message.allowSleep,body.sleepSpeedLimit=message.sleepSpeedLimit,body.sleepTimeLimit=message.sleepTimeLimit,body.mass!==message.mass&&(body.mass=message.mass,body.updateMassProperties()),body},module.exports.deserializeInterpBodyUpdate=function(message1,message2,body,mix){var weight1=1-mix,weight2=mix;body.position.set(message1.position[0]*weight1+message2.position[0]*weight2,message1.position[1]*weight1+message2.position[1]*weight2,message1.position[2]*weight1+message2.position[2]*weight2);var quaternion=mathUtils.slerp(message1.quaternion,message2.quaternion,mix);return body.quaternion.set(quaternion[0],quaternion[1],quaternion[2],quaternion[3]),body.velocity.set(message1.velocity[0]*weight1+message2.velocity[0]*weight2,message1.velocity[1]*weight1+message2.velocity[1]*weight2,message1.velocity[2]*weight1+message2.velocity[2]*weight2),body.angularVelocity.set(message1.angularVelocity[0]*weight1+message2.angularVelocity[0]*weight2,message1.angularVelocity[1]*weight1+message2.angularVelocity[1]*weight2,message1.angularVelocity[2]*weight1+message2.angularVelocity[2]*weight2),body.linearDamping=message2.linearDamping,body.angularDamping=message2.angularDamping,body.fixedRotation=message2.fixedRotation,body.allowSleep=message2.allowSleep,body.sleepSpeedLimit=message2.sleepSpeedLimit,body.sleepTimeLimit=message2.sleepTimeLimit,body.mass!==message2.mass&&(body.mass=message2.mass,body.updateMassProperties()),body},module.exports.deserializeBody=function(message){for(var shapeMsg,body=new CANNON.Body({mass:message.mass,position:deserializeVec3(message.position),quaternion:deserializeQuaternion(message.quaternion),velocity:deserializeVec3(message.velocity),angularVelocity:deserializeVec3(message.angularVelocity),linearDamping:message.linearDamping,angularDamping:message.angularDamping,fixedRotation:message.fixedRotation,allowSleep:message.allowSleep,sleepSpeedLimit:message.sleepSpeedLimit,sleepTimeLimit:message.sleepTimeLimit}),i=0;shapeMsg=message.shapes[i];i++)body.addShape(deserializeShape(shapeMsg),deserializeVec3(message.shapeOffsets[i]),deserializeQuaternion(message.shapeOrientations[i]));return body[ID]=message.id,body},module.exports.serializeShape=serializeShape,module.exports.deserializeShape=deserializeShape,module.exports.serializeConstraint=function(constraint){var message={id:constraint[ID],type:constraint.type,maxForce:constraint.maxForce,bodyA:constraint.bodyA[ID],bodyB:constraint.bodyB[ID]};switch(constraint.type){case"LockConstraint":break;case"DistanceConstraint":message.distance=constraint.distance;break;case"HingeConstraint":case"ConeTwistConstraint":message.axisA=serializeVec3(constraint.axisA),message.axisB=serializeVec3(constraint.axisB),message.pivotA=serializeVec3(constraint.pivotA),message.pivotB=serializeVec3(constraint.pivotB);break;case"PointToPointConstraint":message.pivotA=serializeVec3(constraint.pivotA),message.pivotB=serializeVec3(constraint.pivotB);break;default:throw new Error("Unexpected constraint type: "+constraint.type+'. You may need to manually set `constraint.type = "FooConstraint";`.')}return message},module.exports.deserializeConstraint=function(message,bodies){var constraint,TypedConstraint=CANNON[message.type],bodyA=bodies[message.bodyA],bodyB=bodies[message.bodyB];switch(message.type){case"LockConstraint":constraint=new CANNON.LockConstraint(bodyA,bodyB,message);break;case"DistanceConstraint":constraint=new CANNON.DistanceConstraint(bodyA,bodyB,message.distance,message.maxForce);break;case"HingeConstraint":case"ConeTwistConstraint":constraint=new TypedConstraint(bodyA,bodyB,{pivotA:deserializeVec3(message.pivotA),pivotB:deserializeVec3(message.pivotB),axisA:deserializeVec3(message.axisA),axisB:deserializeVec3(message.axisB),maxForce:message.maxForce});break;case"PointToPointConstraint":constraint=new CANNON.PointToPointConstraint(bodyA,deserializeVec3(message.pivotA),bodyB,deserializeVec3(message.pivotB),message.maxForce);break;default:throw new Error("Unexpected constraint type: "+message.type)}return constraint[ID]=message.id,constraint},module.exports.serializeContact=function(contact){return{bi:contact.bi[ID],bj:contact.bj[ID],ni:serializeVec3(contact.ni),ri:serializeVec3(contact.ri),rj:serializeVec3(contact.rj)}},module.exports.deserializeContact=function(message,bodies){return{bi:bodies[message.bi],bj:bodies[message.bj],ni:deserializeVec3(message.ni),ri:deserializeVec3(message.ri),rj:deserializeVec3(message.rj)}},module.exports.serializeVec3=serializeVec3,module.exports.deserializeVec3=deserializeVec3,module.exports.serializeQuaternion=serializeQuaternion,module.exports.deserializeQuaternion=deserializeQuaternion},{"./math":30,"cannon-es":5}]},{},[1]); \ No newline at end of file +!function(){return function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){return o(e[i][1][r]||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i{const counterValue=document.createElement("div");counterValue.classList.add("rs-counter-value"),counterValue.innerHTML="...",this.counter.appendChild(counterValue),this.counterValues[property]=counterValue}),this.updateData=this.updateData.bind(this),this.el.addEventListener(this.data.event,this.updateData),this.splitCache={}):console.warn(`Couldn't find stats group ${groupComponentName}`)},updateData(e){this.data.properties.forEach(property=>{const split=this.splitDot(property);let value=e.detail;for(i=0;i{this.outputDetail[property]={}}),this.statsReceived=this.statsReceived.bind(this),this.el.addEventListener(this.data.inEvent,this.statsReceived)},resetData(){this.counter=0,this.data.properties.forEach(property=>{this.statsData[property]=[]})},statsReceived(e){this.updateData(e.detail),this.counter++,this.counter===this.data.outputFrequency&&(this.outputData(),this.resetData())},updateData(detail){this.data.properties.forEach(property=>{let value=detail;value=value[property],this.statsData[property].push(value)})},outputData(){this.data.properties.forEach(property=>{this.data.outputs.forEach(output=>{this.outputDetail[property][output]=this.computeOutput(output,this.statsData[property])})}),this.data.outEvent&&this.el.emit(this.data.outEvent,this.outputDetail),this.data.outputToConsole&&console.log(this.data.outputToConsole,this.outputDetail)},computeOutput(outputInstruction,data){const outputInstructions=outputInstruction.split("__");let output;switch(outputInstructions[0]){case"mean":output=data.reduce((a,b)=>a+b,0)/data.length;break;case"max":output=Math.max(...data);break;case"min":output=Math.min(...data);break;case"percentile":const sorted=data.sort((a,b)=>a-b),proportion=+outputInstructions[1].replace("_",".")/100,position=(data.length-1)*proportion,base=Math.floor(position),delta=position-base;output=void 0!==sorted[base+1]?sorted[base]+delta*(sorted[base+1]-sorted[base]):sorted[base]}return output.toFixed(2)}})},{}],4:[function(require,module,exports){THREE.AmmoDebugConstants={NoDebug:0,DrawWireframe:1,DrawAabb:2,DrawFeaturesText:4,DrawContactPoints:8,NoDeactivation:16,NoHelpText:32,DrawText:64,ProfileTimings:128,EnableSatComparison:256,DisableBulletLCP:512,EnableCCD:1024,DrawConstraints:2048,DrawConstraintLimits:4096,FastWireframe:8192,DrawNormals:16384,DrawOnTop:32768,MAX_DEBUG_DRAW_MODE:4294967295},THREE.AmmoDebugDrawer=function(scene,world,options){this.scene=scene,this.world=world,options=options||{},this.debugDrawMode=options.debugDrawMode||THREE.AmmoDebugConstants.DrawWireframe;var drawOnTop=this.debugDrawMode&THREE.AmmoDebugConstants.DrawOnTop||!1,maxBufferSize=options.maxBufferSize||1e6;this.geometry=new THREE.BufferGeometry;var vertices=new Float32Array(3*maxBufferSize),colors=new Float32Array(3*maxBufferSize);this.geometry.setAttribute("position",new THREE.BufferAttribute(vertices,3).setUsage(THREE.DynamicDrawUsage)),this.geometry.setAttribute("color",new THREE.BufferAttribute(colors,3).setUsage(THREE.DynamicDrawUsage)),this.index=0;var material=new THREE.LineBasicMaterial({vertexColors:!0,depthTest:!drawOnTop});this.mesh=new THREE.LineSegments(this.geometry,material),drawOnTop&&(this.mesh.renderOrder=999),this.mesh.frustumCulled=!1,this.enabled=!1,this.debugDrawer=new Ammo.DebugDrawer,this.debugDrawer.drawLine=this.drawLine.bind(this),this.debugDrawer.drawContactPoint=this.drawContactPoint.bind(this),this.debugDrawer.reportErrorWarning=this.reportErrorWarning.bind(this),this.debugDrawer.draw3dText=this.draw3dText.bind(this),this.debugDrawer.setDebugMode=this.setDebugMode.bind(this),this.debugDrawer.getDebugMode=this.getDebugMode.bind(this),this.debugDrawer.enable=this.enable.bind(this),this.debugDrawer.disable=this.disable.bind(this),this.debugDrawer.update=this.update.bind(this),this.world.setDebugDrawer(this.debugDrawer)},THREE.AmmoDebugDrawer.prototype=function(){return this.debugDrawer},THREE.AmmoDebugDrawer.prototype.enable=function(){this.enabled=!0,this.scene.add(this.mesh)},THREE.AmmoDebugDrawer.prototype.disable=function(){this.enabled=!1,this.scene.remove(this.mesh)},THREE.AmmoDebugDrawer.prototype.update=function(){this.enabled&&(0!=this.index&&(this.geometry.attributes.position.needsUpdate=!0,this.geometry.attributes.color.needsUpdate=!0),this.index=0,this.world.debugDrawWorld(),this.geometry.setDrawRange(0,this.index))},THREE.AmmoDebugDrawer.prototype.drawLine=function(from,to,color){const heap=Ammo.HEAPF32,r=heap[(color+0)/4],g=heap[(color+4)/4],b=heap[(color+8)/4],fromX=heap[(from+0)/4],fromY=heap[(from+4)/4],fromZ=heap[(from+8)/4];this.geometry.attributes.position.setXYZ(this.index,fromX,fromY,fromZ),this.geometry.attributes.color.setXYZ(this.index++,r,g,b);const toX=heap[(to+0)/4],toY=heap[(to+4)/4],toZ=heap[(to+8)/4];this.geometry.attributes.position.setXYZ(this.index,toX,toY,toZ),this.geometry.attributes.color.setXYZ(this.index++,r,g,b)},THREE.AmmoDebugDrawer.prototype.drawContactPoint=function(pointOnB,normalOnB,distance,lifeTime,color){const heap=Ammo.HEAPF32,r=heap[(color+0)/4],g=heap[(color+4)/4],b=heap[(color+8)/4],x=heap[(pointOnB+0)/4],y=heap[(pointOnB+4)/4],z=heap[(pointOnB+8)/4];this.geometry.attributes.position.setXYZ(this.index,x,y,z),this.geometry.attributes.color.setXYZ(this.index++,r,g,b);const dx=heap[(normalOnB+0)/4]*distance,dy=heap[(normalOnB+4)/4]*distance,dz=heap[(normalOnB+8)/4]*distance;this.geometry.attributes.position.setXYZ(this.index,x+dx,y+dy,z+dz),this.geometry.attributes.color.setXYZ(this.index++,r,g,b)},THREE.AmmoDebugDrawer.prototype.reportErrorWarning=function(warningString){Ammo.hasOwnProperty("Pointer_stringify")?console.warn(Ammo.Pointer_stringify(warningString)):this.warnedOnce||(this.warnedOnce=!0,console.warn("Cannot print warningString, please rebuild Ammo.js using 'debug' flag"))},THREE.AmmoDebugDrawer.prototype.draw3dText=function(location,textString){console.warn("TODO: draw3dText")},THREE.AmmoDebugDrawer.prototype.setDebugMode=function(debugMode){this.debugDrawMode=debugMode},THREE.AmmoDebugDrawer.prototype.getDebugMode=function(){return this.debugDrawMode}},{}],5:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.World=exports.Vec3Pool=exports.Vec3=exports.Trimesh=exports.Transform=exports.Spring=exports.SplitSolver=exports.Sphere=exports.Solver=exports.Shape=exports.SPHSystem=exports.SHAPE_TYPES=exports.SAPBroadphase=exports.RotationalMotorEquation=exports.RotationalEquation=exports.RigidVehicle=exports.RaycastVehicle=exports.RaycastResult=exports.Ray=exports.RAY_MODES=exports.Quaternion=exports.Pool=exports.PointToPointConstraint=exports.Plane=exports.Particle=exports.ObjectCollisionMatrix=exports.Narrowphase=exports.NaiveBroadphase=exports.Material=exports.Mat3=exports.LockConstraint=exports.JacobianElement=exports.HingeConstraint=exports.Heightfield=exports.GridBroadphase=exports.GSSolver=exports.FrictionEquation=exports.EventTarget=exports.Equation=exports.DistanceConstraint=exports.Cylinder=exports.ConvexPolyhedron=exports.ContactMaterial=exports.ContactEquation=exports.Constraint=exports.ConeTwistConstraint=exports.COLLISION_TYPES=exports.Broadphase=exports.Box=exports.Body=exports.BODY_TYPES=exports.BODY_SLEEP_STATES=exports.ArrayCollisionMatrix=exports.AABB=void 0;exports.ObjectCollisionMatrix=class{constructor(){this.matrix={}}get(bi,bj){let{id:i}=bi,{id:j}=bj;if(j>i){const temp=j;j=i,i=temp}return i+"-"+j in this.matrix}set(bi,bj,value){let{id:i}=bi,{id:j}=bj;if(j>i){const temp=j;j=i,i=temp}value?this.matrix[i+"-"+j]=!0:delete this.matrix[i+"-"+j]}reset(){this.matrix={}}setNumObjects(n){}};class Mat3{constructor(elements=[0,0,0,0,0,0,0,0,0]){this.elements=elements}identity(){const e=this.elements;e[0]=1,e[1]=0,e[2]=0,e[3]=0,e[4]=1,e[5]=0,e[6]=0,e[7]=0,e[8]=1}setZero(){const e=this.elements;e[0]=0,e[1]=0,e[2]=0,e[3]=0,e[4]=0,e[5]=0,e[6]=0,e[7]=0,e[8]=0}setTrace(vector){const e=this.elements;e[0]=vector.x,e[4]=vector.y,e[8]=vector.z}getTrace(target=new Vec3){const e=this.elements;target.x=e[0],target.y=e[4],target.z=e[8]}vmult(v,target=new Vec3){const e=this.elements,x=v.x,y=v.y,z=v.z;return target.x=e[0]*x+e[1]*y+e[2]*z,target.y=e[3]*x+e[4]*y+e[5]*z,target.z=e[6]*x+e[7]*y+e[8]*z,target}smult(s){for(let i=0;i0){const invN=1/n;this.x*=invN,this.y*=invN,this.z*=invN}else this.x=0,this.y=0,this.z=0;return n}unit(target=new Vec3){const x=this.x,y=this.y,z=this.z;let ninv=Math.sqrt(x*x+y*y+z*z);return ninv>0?(ninv=1/ninv,target.x=x*ninv,target.y=y*ninv,target.z=z*ninv):(target.x=1,target.y=0,target.z=0),target}length(){const x=this.x,y=this.y,z=this.z;return Math.sqrt(x*x+y*y+z*z)}lengthSquared(){return this.dot(this)}distanceTo(p){const x=this.x,y=this.y,z=this.z,px=p.x,py=p.y,pz=p.z;return Math.sqrt((px-x)*(px-x)+(py-y)*(py-y)+(pz-z)*(pz-z))}distanceSquared(p){const x=this.x,y=this.y,z=this.z,px=p.x,py=p.y,pz=p.z;return(px-x)*(px-x)+(py-y)*(py-y)+(pz-z)*(pz-z)}scale(scalar,target=new Vec3){const x=this.x,y=this.y,z=this.z;return target.x=scalar*x,target.y=scalar*y,target.z=scalar*z,target}vmul(vector,target=new Vec3){return target.x=vector.x*this.x,target.y=vector.y*this.y,target.z=vector.z*this.z,target}addScaledVector(scalar,vector,target=new Vec3){return target.x=this.x+scalar*vector.x,target.y=this.y+scalar*vector.y,target.z=this.z+scalar*vector.z,target}dot(vector){return this.x*vector.x+this.y*vector.y+this.z*vector.z}isZero(){return 0===this.x&&0===this.y&&0===this.z}negate(target=new Vec3){return target.x=-this.x,target.y=-this.y,target.z=-this.z,target}tangents(t1,t2){const norm=this.length();if(norm>0){const n=Vec3_tangents_n,inorm=1/norm;n.set(this.x*inorm,this.y*inorm,this.z*inorm);const randVec=Vec3_tangents_randVec;Math.abs(n.x)<.9?(randVec.set(1,0,0),n.cross(randVec,t1)):(randVec.set(0,1,0),n.cross(randVec,t1)),n.cross(t1,t2)}else t1.set(1,0,0),t2.set(0,1,0)}toString(){return this.x+","+this.y+","+this.z}toArray(){return[this.x,this.y,this.z]}copy(vector){return this.x=vector.x,this.y=vector.y,this.z=vector.z,this}lerp(vector,t,target){const x=this.x,y=this.y,z=this.z;target.x=x+(vector.x-x)*t,target.y=y+(vector.y-y)*t,target.z=z+(vector.z-z)*t}almostEquals(vector,precision=1e-6){return!(Math.abs(this.x-vector.x)>precision||Math.abs(this.y-vector.y)>precision||Math.abs(this.z-vector.z)>precision)}almostZero(precision=1e-6){return!(Math.abs(this.x)>precision||Math.abs(this.y)>precision||Math.abs(this.z)>precision)}isAntiparallelTo(vector,precision){return this.negate(antip_neg),antip_neg.almostEquals(vector,precision)}clone(){return new Vec3(this.x,this.y,this.z)}}exports.Vec3=Vec3,Vec3.ZERO=new Vec3(0,0,0),Vec3.UNIT_X=new Vec3(1,0,0),Vec3.UNIT_Y=new Vec3(0,1,0),Vec3.UNIT_Z=new Vec3(0,0,1);const Vec3_tangents_n=new Vec3,Vec3_tangents_randVec=new Vec3,antip_neg=new Vec3;class AABB{constructor(options={}){this.lowerBound=new Vec3,this.upperBound=new Vec3,options.lowerBound&&this.lowerBound.copy(options.lowerBound),options.upperBound&&this.upperBound.copy(options.upperBound)}setFromPoints(points,position,quaternion,skinSize){const l=this.lowerBound,u=this.upperBound,q=quaternion;l.copy(points[0]),q&&q.vmult(l,l),u.copy(l);for(let i=1;iu.x&&(u.x=p.x),p.xu.y&&(u.y=p.y),p.yu.z&&(u.z=p.z),p.z=u2.x&&l1.y<=l2.y&&u1.y>=u2.y&&l1.z<=l2.z&&u1.z>=u2.z}getCorners(a,b,c,d,e,f,g,h){const l=this.lowerBound,u=this.upperBound;a.copy(l),b.set(u.x,l.y,l.z),c.set(u.x,u.y,l.z),d.set(l.x,u.y,u.z),e.set(u.x,l.y,u.z),f.set(l.x,u.y,l.z),g.set(l.x,l.y,u.z),h.copy(u)}toLocalFrame(frame,target){const corners=transformIntoFrame_corners,a=corners[0],b=corners[1],c=corners[2],d=corners[3],e=corners[4],f=corners[5],g=corners[6],h=corners[7];this.getCorners(a,b,c,d,e,f,g,h);for(let i=0;8!==i;i++){const corner=corners[i];frame.pointToLocal(corner,corner)}return target.setFromPoints(corners)}toWorldFrame(frame,target){const corners=transformIntoFrame_corners,a=corners[0],b=corners[1],c=corners[2],d=corners[3],e=corners[4],f=corners[5],g=corners[6],h=corners[7];this.getCorners(a,b,c,d,e,f,g,h);for(let i=0;8!==i;i++){const corner=corners[i];frame.pointToWorld(corner,corner)}return target.setFromPoints(corners)}overlapsRay(ray){const{direction:direction,from:from}=ray,dirFracX=1/direction.x,dirFracY=1/direction.y,dirFracZ=1/direction.z,t1=(this.lowerBound.x-from.x)*dirFracX,t2=(this.upperBound.x-from.x)*dirFracX,t3=(this.lowerBound.y-from.y)*dirFracY,t4=(this.upperBound.y-from.y)*dirFracY,t5=(this.lowerBound.z-from.z)*dirFracZ,t6=(this.upperBound.z-from.z)*dirFracZ,tmin=Math.max(Math.max(Math.min(t1,t2),Math.min(t3,t4)),Math.min(t5,t6)),tmax=Math.min(Math.min(Math.max(t1,t2),Math.max(t3,t4)),Math.max(t5,t6));return!(tmax<0)&&!(tmin>tmax)}}exports.AABB=AABB;const tmp=new Vec3,transformIntoFrame_corners=[new Vec3,new Vec3,new Vec3,new Vec3,new Vec3,new Vec3,new Vec3,new Vec3];class ArrayCollisionMatrix{constructor(){this.matrix=[]}get(bi,bj){let{index:i}=bi,{index:j}=bj;if(j>i){const temp=j;j=i,i=temp}return this.matrix[(i*(i+1)>>1)+j-1]}set(bi,bj,value){let{index:i}=bi,{index:j}=bj;if(j>i){const temp=j;j=i,i=temp}this.matrix[(i*(i+1)>>1)+j-1]=value?1:0}reset(){for(let i=0,l=this.matrix.length;i!==l;i++)this.matrix[i]=0}setNumObjects(n){this.matrix.length=n*(n-1)>>1}}exports.ArrayCollisionMatrix=ArrayCollisionMatrix;class EventTarget{constructor(){}addEventListener(type,listener){void 0===this._listeners&&(this._listeners={});const listeners=this._listeners;return void 0===listeners[type]&&(listeners[type]=[]),listeners[type].includes(listener)||listeners[type].push(listener),this}hasEventListener(type,listener){if(void 0===this._listeners)return!1;const listeners=this._listeners;return!(void 0===listeners[type]||!listeners[type].includes(listener))}hasAnyEventListener(type){if(void 0===this._listeners)return!1;return void 0!==this._listeners[type]}removeEventListener(type,listener){if(void 0===this._listeners)return this;const listeners=this._listeners;if(void 0===listeners[type])return this;const index=listeners[type].indexOf(listener);return-1!==index&&listeners[type].splice(index,1),this}dispatchEvent(event){if(void 0===this._listeners)return this;const listenerArray=this._listeners[event.type];if(void 0!==listenerArray){event.target=this;for(let i=0,l=listenerArray.length;i.499&&(heading=2*Math.atan2(x,w),attitude=Math.PI/2,bank=0),test<-.499&&(heading=-2*Math.atan2(x,w),attitude=-Math.PI/2,bank=0),void 0===heading){const sqx=x*x,sqy=y*y,sqz=z*z;heading=Math.atan2(2*y*w-2*x*z,1-2*sqy-2*sqz),attitude=Math.asin(2*test),bank=Math.atan2(2*x*w-2*y*z,1-2*sqx-2*sqz)}break;default:throw new Error("Euler order "+order+" not supported yet.")}target.y=heading,target.z=attitude,target.x=bank}setFromEuler(x,y,z,order="XYZ"){const c1=Math.cos(x/2),c2=Math.cos(y/2),c3=Math.cos(z/2),s1=Math.sin(x/2),s2=Math.sin(y/2),s3=Math.sin(z/2);return"XYZ"===order?(this.x=s1*c2*c3+c1*s2*s3,this.y=c1*s2*c3-s1*c2*s3,this.z=c1*c2*s3+s1*s2*c3,this.w=c1*c2*c3-s1*s2*s3):"YXZ"===order?(this.x=s1*c2*c3+c1*s2*s3,this.y=c1*s2*c3-s1*c2*s3,this.z=c1*c2*s3-s1*s2*c3,this.w=c1*c2*c3+s1*s2*s3):"ZXY"===order?(this.x=s1*c2*c3-c1*s2*s3,this.y=c1*s2*c3+s1*c2*s3,this.z=c1*c2*s3+s1*s2*c3,this.w=c1*c2*c3-s1*s2*s3):"ZYX"===order?(this.x=s1*c2*c3-c1*s2*s3,this.y=c1*s2*c3+s1*c2*s3,this.z=c1*c2*s3-s1*s2*c3,this.w=c1*c2*c3+s1*s2*s3):"YZX"===order?(this.x=s1*c2*c3+c1*s2*s3,this.y=c1*s2*c3+s1*c2*s3,this.z=c1*c2*s3-s1*s2*c3,this.w=c1*c2*c3-s1*s2*s3):"XZY"===order&&(this.x=s1*c2*c3-c1*s2*s3,this.y=c1*s2*c3-s1*c2*s3,this.z=c1*c2*s3+s1*s2*c3,this.w=c1*c2*c3+s1*s2*s3),this}clone(){return new Quaternion(this.x,this.y,this.z,this.w)}slerp(toQuat,t,target=new Quaternion){const ax=this.x,ay=this.y,az=this.z,aw=this.w;let omega,cosom,sinom,scale0,scale1,bx=toQuat.x,by=toQuat.y,bz=toQuat.z,bw=toQuat.w;return(cosom=ax*bx+ay*by+az*bz+aw*bw)<0&&(cosom=-cosom,bx=-bx,by=-by,bz=-bz,bw=-bw),1-cosom>1e-6?(omega=Math.acos(cosom),sinom=Math.sin(omega),scale0=Math.sin((1-t)*omega)/sinom,scale1=Math.sin(t*omega)/sinom):(scale0=1-t,scale1=t),target.x=scale0*ax+scale1*bx,target.y=scale0*ay+scale1*by,target.z=scale0*az+scale1*bz,target.w=scale0*aw+scale1*bw,target}integrate(angularVelocity,dt,angularFactor,target=new Quaternion){const ax=angularVelocity.x*angularFactor.x,ay=angularVelocity.y*angularFactor.y,az=angularVelocity.z*angularFactor.z,bx=this.x,by=this.y,bz=this.z,bw=this.w,half_dt=.5*dt;return target.x+=half_dt*(ax*bw+ay*bz-az*by),target.y+=half_dt*(ay*bw+az*bx-ax*bz),target.z+=half_dt*(az*bw+ax*by-ay*bx),target.w+=half_dt*(-ax*bx-ay*by-az*bz),target}}exports.Quaternion=Quaternion;const sfv_t1=new Vec3,sfv_t2=new Vec3,SHAPE_TYPES=exports.SHAPE_TYPES={SPHERE:1,PLANE:2,BOX:4,COMPOUND:8,CONVEXPOLYHEDRON:16,HEIGHTFIELD:32,PARTICLE:64,CYLINDER:128,TRIMESH:256};class Shape{constructor(options={}){this.id=Shape.idCounter++,this.type=options.type||0,this.boundingSphereRadius=0,this.collisionResponse=!options.collisionResponse||options.collisionResponse,this.collisionFilterGroup=void 0!==options.collisionFilterGroup?options.collisionFilterGroup:1,this.collisionFilterMask=void 0!==options.collisionFilterMask?options.collisionFilterMask:-1,this.material=options.material?options.material:null,this.body=null}updateBoundingSphereRadius(){throw"computeBoundingSphereRadius() not implemented for shape type "+this.type}volume(){throw"volume() not implemented for shape type "+this.type}calculateLocalInertia(mass,target){throw"calculateLocalInertia() not implemented for shape type "+this.type}calculateWorldAABB(pos,quat,min,max){throw"calculateWorldAABB() not implemented for shape type "+this.type}}exports.Shape=Shape,Shape.idCounter=0,Shape.types=SHAPE_TYPES;class Transform{constructor(options={}){this.position=new Vec3,this.quaternion=new Quaternion,options.position&&this.position.copy(options.position),options.quaternion&&this.quaternion.copy(options.quaternion)}pointToLocal(worldPoint,result){return Transform.pointToLocalFrame(this.position,this.quaternion,worldPoint,result)}pointToWorld(localPoint,result){return Transform.pointToWorldFrame(this.position,this.quaternion,localPoint,result)}vectorToWorldFrame(localVector,result=new Vec3){return this.quaternion.vmult(localVector,result),result}static pointToLocalFrame(position,quaternion,worldPoint,result=new Vec3){return worldPoint.vsub(position,result),quaternion.conjugate(tmpQuat),tmpQuat.vmult(result,result),result}static pointToWorldFrame(position,quaternion,localPoint,result=new Vec3){return quaternion.vmult(localPoint,result),result.vadd(position,result),result}static vectorToWorldFrame(quaternion,localVector,result=new Vec3){return quaternion.vmult(localVector,result),result}static vectorToLocalFrame(position,quaternion,worldVector,result=new Vec3){return quaternion.w*=-1,quaternion.vmult(worldVector,result),quaternion.w*=-1,result}}exports.Transform=Transform;const tmpQuat=new Quaternion;class ConvexPolyhedron extends Shape{constructor(props={}){const{vertices:vertices=[],faces:faces=[],normals:normals=[],axes:axes,boundingSphereRadius:boundingSphereRadius}=props;super({type:Shape.types.CONVEXPOLYHEDRON}),this.vertices=vertices,this.faces=faces,this.faceNormals=normals,0===this.faceNormals.length&&this.computeNormals(),boundingSphereRadius?this.boundingSphereRadius=boundingSphereRadius:this.updateBoundingSphereRadius(),this.worldVertices=[],this.worldVerticesNeedsUpdate=!0,this.worldFaceNormals=[],this.worldFaceNormalsNeedsUpdate=!0,this.uniqueAxes=axes?axes.slice():null,this.uniqueEdges=[],this.computeEdges()}computeEdges(){const faces=this.faces,vertices=this.vertices,edges=this.uniqueEdges;edges.length=0;const edge=new Vec3;for(let i=0;i!==faces.length;i++){const face=faces[i],numVertices=face.length;for(let j=0;j!==numVertices;j++){const k=(j+1)%numVertices;vertices[face[j]].vsub(vertices[face[k]],edge),edge.normalize();let found=!1;for(let p=0;p!==edges.length;p++)if(edges[p].almostEquals(edge)||edges[p].almostEquals(edge)){found=!0;break}found||edges.push(edge.clone())}}}computeNormals(){this.faceNormals.length=this.faces.length;for(let i=0;idmax&&(dmax=d,closestFaceB=face)}const worldVertsB1=[];for(let i=0;i=0&&this.clipFaceAgainstHull(separatingNormal,posA,quatA,worldVertsB1,minDist,maxDist,result)}findSeparatingAxis(hullB,posA,quatA,posB,quatB,target,faceListA,faceListB){const faceANormalWS3=new Vec3,Worldnormal1=new Vec3,deltaC=new Vec3,worldEdge0=new Vec3,worldEdge1=new Vec3,Cross=new Vec3;let dmin=Number.MAX_VALUE;const hullA=this;if(hullA.uniqueAxes)for(let i=0;i!==hullA.uniqueAxes.length;i++){quatA.vmult(hullA.uniqueAxes[i],faceANormalWS3);const d=hullA.testSepAxis(faceANormalWS3,hullB,posA,quatA,posB,quatB);if(!1===d)return!1;d0&&target.negate(target),!0}testSepAxis(axis,hullB,posA,quatA,posB,quatB){ConvexPolyhedron.project(this,axis,posA,quatA,maxminA),ConvexPolyhedron.project(hullB,axis,posB,quatB,maxminB);const maxA=maxminA[0],minA=maxminA[1],maxB=maxminB[0],minB=maxminB[1];if(maxA0?1/mass:0,this.material=options.material||null,this.linearDamping="number"==typeof options.linearDamping?options.linearDamping:.01,this.type=mass<=0?Body.STATIC:Body.DYNAMIC,typeof options.type==typeof Body.STATIC&&(this.type=options.type),this.allowSleep=void 0===options.allowSleep||options.allowSleep,this.sleepState=0,this.sleepSpeedLimit=void 0!==options.sleepSpeedLimit?options.sleepSpeedLimit:.1,this.sleepTimeLimit=void 0!==options.sleepTimeLimit?options.sleepTimeLimit:1,this.timeLastSleepy=0,this.wakeUpAfterNarrowphase=!1,this.torque=new Vec3,this.quaternion=new Quaternion,this.initQuaternion=new Quaternion,this.previousQuaternion=new Quaternion,this.interpolatedQuaternion=new Quaternion,options.quaternion&&(this.quaternion.copy(options.quaternion),this.initQuaternion.copy(options.quaternion),this.previousQuaternion.copy(options.quaternion),this.interpolatedQuaternion.copy(options.quaternion)),this.angularVelocity=new Vec3,options.angularVelocity&&this.angularVelocity.copy(options.angularVelocity),this.initAngularVelocity=new Vec3,this.shapes=[],this.shapeOffsets=[],this.shapeOrientations=[],this.inertia=new Vec3,this.invInertia=new Vec3,this.invInertiaWorld=new Mat3,this.invMassSolve=0,this.invInertiaSolve=new Vec3,this.invInertiaWorldSolve=new Mat3,this.fixedRotation=void 0!==options.fixedRotation&&options.fixedRotation,this.angularDamping=void 0!==options.angularDamping?options.angularDamping:.01,this.linearFactor=new Vec3(1,1,1),options.linearFactor&&this.linearFactor.copy(options.linearFactor),this.angularFactor=new Vec3(1,1,1),options.angularFactor&&this.angularFactor.copy(options.angularFactor),this.aabb=new AABB,this.aabbNeedsUpdate=!0,this.boundingRadius=0,this.wlambda=new Vec3,options.shape&&this.addShape(options.shape),this.updateMassProperties()}wakeUp(){const prevState=this.sleepState;this.sleepState=0,this.wakeUpAfterNarrowphase=!1,prevState===Body.SLEEPING&&this.dispatchEvent(Body.wakeupEvent)}sleep(){this.sleepState=Body.SLEEPING,this.velocity.set(0,0,0),this.angularVelocity.set(0,0,0),this.wakeUpAfterNarrowphase=!1}sleepTick(time){if(this.allowSleep){const sleepState=this.sleepState,speedSquared=this.velocity.lengthSquared()+this.angularVelocity.lengthSquared(),speedLimitSquared=this.sleepSpeedLimit**2;sleepState===Body.AWAKE&&speedSquaredspeedLimitSquared?this.wakeUp():sleepState===Body.SLEEPY&&time-this.timeLastSleepy>this.sleepTimeLimit&&(this.sleep(),this.dispatchEvent(Body.sleepEvent))}}updateSolveMassProperties(){this.sleepState===Body.SLEEPING||this.type===Body.KINEMATIC?(this.invMassSolve=0,this.invInertiaSolve.setZero(),this.invInertiaWorldSolve.setZero()):(this.invMassSolve=this.invMass,this.invInertiaSolve.copy(this.invInertia),this.invInertiaWorldSolve.copy(this.invInertiaWorld))}pointToLocalFrame(worldPoint,result=new Vec3){return worldPoint.vsub(this.position,result),this.quaternion.conjugate().vmult(result,result),result}vectorToLocalFrame(worldVector,result=new Vec3){return this.quaternion.conjugate().vmult(worldVector,result),result}pointToWorldFrame(localPoint,result=new Vec3){return this.quaternion.vmult(localPoint,result),result.vadd(this.position,result),result}vectorToWorldFrame(localVector,result=new Vec3){return this.quaternion.vmult(localVector,result),result}addShape(shape,_offset,_orientation){const offset=new Vec3,orientation=new Quaternion;return _offset&&offset.copy(_offset),_orientation&&orientation.copy(_orientation),this.shapes.push(shape),this.shapeOffsets.push(offset),this.shapeOrientations.push(orientation),this.updateMassProperties(),this.updateBoundingRadius(),this.aabbNeedsUpdate=!0,shape.body=this,this}updateBoundingRadius(){const shapes=this.shapes,shapeOffsets=this.shapeOffsets,N=shapes.length;let radius=0;for(let i=0;i!==N;i++){const shape=shapes[i];shape.updateBoundingSphereRadius();const offset=shapeOffsets[i].length(),r=shape.boundingSphereRadius;offset+r>radius&&(radius=offset+r)}this.boundingRadius=radius}computeAABB(){const shapes=this.shapes,shapeOffsets=this.shapeOffsets,shapeOrientations=this.shapeOrientations,N=shapes.length,offset=tmpVec,orientation=tmpQuat$1,bodyQuat=this.quaternion,aabb=this.aabb,shapeAABB=computeAABB_shapeAABB;for(let i=0;i!==N;i++){const shape=shapes[i];bodyQuat.vmult(shapeOffsets[i],offset),offset.vadd(this.position,offset),bodyQuat.mult(shapeOrientations[i],orientation),shape.calculateWorldAABB(offset,orientation,shapeAABB.lowerBound,shapeAABB.upperBound),0===i?aabb.copy(shapeAABB):aabb.extend(shapeAABB)}this.aabbNeedsUpdate=!1}updateInertiaWorld(force){const I=this.invInertia;if(I.x!==I.y||I.y!==I.z||force){const m1=uiw_m1,m2=uiw_m2;m1.setRotationFromQuaternion(this.quaternion),m1.transpose(m2),m1.scale(I,m1),m1.mmult(m2,this.invInertiaWorld)}else;}applyForce(force,relativePoint){if(this.type!==Body.DYNAMIC)return;const rotForce=Body_applyForce_rotForce;relativePoint.cross(force,rotForce),this.force.vadd(force,this.force),this.torque.vadd(rotForce,this.torque)}applyLocalForce(localForce,localPoint){if(this.type!==Body.DYNAMIC)return;const worldForce=Body_applyLocalForce_worldForce,relativePointWorld=Body_applyLocalForce_relativePointWorld;this.vectorToWorldFrame(localForce,worldForce),this.vectorToWorldFrame(localPoint,relativePointWorld),this.applyForce(worldForce,relativePointWorld)}applyImpulse(impulse,relativePoint){if(this.type!==Body.DYNAMIC)return;const r=relativePoint,velo=Body_applyImpulse_velo;velo.copy(impulse),velo.scale(this.invMass,velo),this.velocity.vadd(velo,this.velocity);const rotVelo=Body_applyImpulse_rotVelo;r.cross(impulse,rotVelo),this.invInertiaWorld.vmult(rotVelo,rotVelo),this.angularVelocity.vadd(rotVelo,this.angularVelocity)}applyLocalImpulse(localImpulse,localPoint){if(this.type!==Body.DYNAMIC)return;const worldImpulse=Body_applyLocalImpulse_worldImpulse,relativePointWorld=Body_applyLocalImpulse_relativePoint;this.vectorToWorldFrame(localImpulse,worldImpulse),this.vectorToWorldFrame(localPoint,relativePointWorld),this.applyImpulse(worldImpulse,relativePointWorld)}updateMassProperties(){const halfExtents=Body_updateMassProperties_halfExtents;this.invMass=this.mass>0?1/this.mass:0;const I=this.inertia,fixed=this.fixedRotation;this.computeAABB(),halfExtents.set((this.aabb.upperBound.x-this.aabb.lowerBound.x)/2,(this.aabb.upperBound.y-this.aabb.lowerBound.y)/2,(this.aabb.upperBound.z-this.aabb.lowerBound.z)/2),Box.calculateInertia(halfExtents,this.mass,I),this.invInertia.set(I.x>0&&!fixed?1/I.x:0,I.y>0&&!fixed?1/I.y:0,I.z>0&&!fixed?1/I.z:0),this.updateInertiaWorld(!0)}getVelocityAtWorldPoint(worldPoint,result){const r=new Vec3;return worldPoint.vsub(this.position,r),this.angularVelocity.cross(r,result),this.velocity.vadd(result,result),result}integrate(dt,quatNormalize,quatNormalizeFast){if(this.previousPosition.copy(this.position),this.previousQuaternion.copy(this.quaternion),this.type!==Body.DYNAMIC&&this.type!==Body.KINEMATIC||this.sleepState===Body.SLEEPING)return;const velo=this.velocity,angularVelo=this.angularVelocity,pos=this.position,force=this.force,torque=this.torque,quat=this.quaternion,invMass=this.invMass,invInertia=this.invInertiaWorld,linearFactor=this.linearFactor,iMdt=invMass*dt;velo.x+=force.x*iMdt*linearFactor.x,velo.y+=force.y*iMdt*linearFactor.y,velo.z+=force.z*iMdt*linearFactor.z;const e=invInertia.elements,angularFactor=this.angularFactor,tx=torque.x*angularFactor.x,ty=torque.y*angularFactor.y,tz=torque.z*angularFactor.z;angularVelo.x+=dt*(e[0]*tx+e[1]*ty+e[2]*tz),angularVelo.y+=dt*(e[3]*tx+e[4]*ty+e[5]*tz),angularVelo.z+=dt*(e[6]*tx+e[7]*ty+e[8]*tz),pos.x+=velo.x*dt,pos.y+=velo.y*dt,pos.z+=velo.z*dt,quat.integrate(this.angularVelocity,dt,this.angularFactor,quat),quatNormalize&&(quatNormalizeFast?quat.normalizeFast():quat.normalize()),this.aabbNeedsUpdate=!0,this.updateInertiaWorld()}}exports.Body=Body,Body.COLLIDE_EVENT_NAME="collide",Body.DYNAMIC=1,Body.STATIC=2,Body.KINEMATIC=4,Body.AWAKE=BODY_SLEEP_STATES.AWAKE,Body.SLEEPY=BODY_SLEEP_STATES.SLEEPY,Body.SLEEPING=BODY_SLEEP_STATES.SLEEPING,Body.idCounter=0,Body.wakeupEvent={type:"wakeup"},Body.sleepyEvent={type:"sleepy"},Body.sleepEvent={type:"sleep"};const tmpVec=new Vec3,tmpQuat$1=new Quaternion,computeAABB_shapeAABB=new AABB,uiw_m1=new Mat3,uiw_m2=new Mat3,Body_applyForce_rotForce=new Vec3,Body_applyLocalForce_worldForce=new Vec3,Body_applyLocalForce_relativePointWorld=new Vec3,Body_applyImpulse_velo=new Vec3,Body_applyImpulse_rotVelo=new Vec3,Body_applyLocalImpulse_worldImpulse=new Vec3,Body_applyLocalImpulse_relativePoint=new Vec3,Body_updateMassProperties_halfExtents=new Vec3;class Broadphase{constructor(){this.world=null,this.useBoundingBoxes=!1,this.dirty=!0}collisionPairs(world,p1,p2){throw new Error("collisionPairs not implemented for this BroadPhase class!")}needBroadphaseCollision(bodyA,bodyB){return 0!=(bodyA.collisionFilterGroup&bodyB.collisionFilterMask)&&0!=(bodyB.collisionFilterGroup&bodyA.collisionFilterMask)&&(0==(bodyA.type&Body.STATIC)&&bodyA.sleepState!==Body.SLEEPING||0==(bodyB.type&Body.STATIC)&&bodyB.sleepState!==Body.SLEEPING)}intersectionTest(bodyA,bodyB,pairs1,pairs2){this.useBoundingBoxes?this.doBoundingBoxBroadphase(bodyA,bodyB,pairs1,pairs2):this.doBoundingSphereBroadphase(bodyA,bodyB,pairs1,pairs2)}doBoundingSphereBroadphase(bodyA,bodyB,pairs1,pairs2){const r=Broadphase_collisionPairs_r;bodyB.position.vsub(bodyA.position,r);const boundingRadiusSum2=(bodyA.boundingRadius+bodyB.boundingRadius)**2;r.lengthSquared(){const dist=new Vec3;bodyA.position.vsub(bodyB.position,dist);const sa=bodyA.shapes[0],sb=bodyB.shapes[0];return Math.pow(sa.boundingSphereRadius+sb.boundingSphereRadius,2)>dist.lengthSquared()});exports.GridBroadphase=class extends Broadphase{constructor(aabbMin=new Vec3(100,100,100),aabbMax=new Vec3(-100,-100,-100),nx=10,ny=10,nz=10){super(),this.nx=nx,this.ny=ny,this.nz=nz,this.aabbMin=aabbMin,this.aabbMax=aabbMax;const nbins=this.nx*this.ny*this.nz;if(nbins<=0)throw"GridBroadphase: Each dimension's n must be >0";this.bins=[],this.binLengths=[],this.bins.length=nbins,this.binLengths.length=nbins;for(let i=0;i=nx&&(xoff0=nx-1),yoff0<0?yoff0=0:yoff0>=ny&&(yoff0=ny-1),zoff0<0?zoff0=0:zoff0>=nz&&(zoff0=nz-1),xoff1<0?xoff1=0:xoff1>=nx&&(xoff1=nx-1),yoff1<0?yoff1=0:yoff1>=ny&&(yoff1=ny-1),zoff1<0?zoff1=0:zoff1>=nz&&(zoff1=nz-1),yoff0*=ystep,zoff0*=zstep,xoff1*=xstep,yoff1*=ystep,zoff1*=zstep;for(let xoff=xoff0*=xstep;xoff<=xoff1;xoff+=xstep)for(let yoff=yoff0;yoff<=yoff1;yoff+=ystep)for(let zoff=zoff0;zoff<=zoff1;zoff+=zstep){const idx=xoff+yoff+zoff;bins[idx][binLengths[idx]++]=bi}}for(let i=0;i!==N;i++){const bi=bodies[i],si=bi.shapes[0];switch(si.type){case SPHERE:{const shape=si,x=bi.position.x,y=bi.position.y,z=bi.position.z,r=shape.radius;addBoxToBins(x-r,y-r,z-r,x+r,y+r,z+r,bi);break}case PLANE:{const shape=si;shape.worldNormalNeedsUpdate&&shape.computeWorldNormal(bi.quaternion);const planeNormal=shape.worldNormal,xreset=xmin+.5*binsizeX-bi.position.x,yreset=ymin+.5*binsizeY-bi.position.y,zreset=zmin+.5*binsizeZ-bi.position.z,d=GridBroadphase_collisionPairs_d;d.set(xreset,yreset,zreset);for(let xi=0,xoff=0;xi!==nx;xi++,xoff+=xstep,d.y=yreset,d.x+=binsizeX)for(let yi=0,yoff=0;yi!==ny;yi++,yoff+=ystep,d.z=zreset,d.y+=binsizeY)for(let zi=0,zoff=0;zi!==nz;zi++,zoff+=zstep,d.z+=binsizeZ)if(d.dot(planeNormal)1){const bin=bins[i];for(let xi=0;xi!==binLength;xi++){const bi=bin[xi];for(let yi=0;yi!==xi;yi++){const bj=bin[yi];this.needBroadphaseCollision(bi,bj)&&this.intersectionTest(bi,bj,pairs1,pairs2)}}}}this.makePairsUnique(pairs1,pairs2)}};const GridBroadphase_collisionPairs_d=new Vec3;class NaiveBroadphase extends Broadphase{constructor(){super()}collisionPairs(world,pairs1,pairs2){const bodies=world.bodies,n=bodies.length;let bi,bj;for(let i=0;i!==n;i++)for(let j=0;j!==i;j++)bi=bodies[i],bj=bodies[j],this.needBroadphaseCollision(bi,bj)&&this.intersectionTest(bi,bj,pairs1,pairs2)}aabbQuery(world,aabb,result=[]){for(let i=0;i{})}intersectWorld(world,options){return this.mode=options.mode||Ray.ANY,this.result=options.result||new RaycastResult,this.skipBackfaces=!!options.skipBackfaces,this.collisionFilterMask=void 0!==options.collisionFilterMask?options.collisionFilterMask:-1,this.collisionFilterGroup=void 0!==options.collisionFilterGroup?options.collisionFilterGroup:-1,this.checkCollisionResponse=void 0===options.checkCollisionResponse||options.checkCollisionResponse,options.from&&this.from.copy(options.from),options.to&&this.to.copy(options.to),this.callback=options.callback||(()=>{}),this.hasHit=!1,this.result.reset(),this.updateDirection(),this.getAABB(tmpAABB),tmpArray.length=0,world.broadphase.aabbQuery(world,tmpAABB,tmpArray),this.intersectBodies(tmpArray),this.hasHit}intersectBody(body,result){result&&(this.result=result,this.updateDirection());const checkCollisionResponse=this.checkCollisionResponse;if(checkCollisionResponse&&!body.collisionResponse)return;if(0==(this.collisionFilterGroup&body.collisionFilterMask)||0==(body.collisionFilterGroup&this.collisionFilterMask))return;const xi=intersectBody_xi,qi=intersectBody_qi;for(let i=0,N=body.shapes.length;ishape.boundingSphereRadius)return;const intersectMethod=this[shape.type];intersectMethod&&intersectMethod.call(this,shape,quat,position,body,shape)}_intersectBox(box,quat,position,body,reportedShape){return this._intersectConvex(box.convexPolyhedronRepresentation,quat,position,body,reportedShape)}_intersectPlane(shape,quat,position,body,reportedShape){const from=this.from,to=this.to,direction=this.direction,worldNormal=new Vec3(0,0,1);quat.vmult(worldNormal,worldNormal);const len=new Vec3;from.vsub(position,len);const planeToFrom=len.dot(worldNormal);if(to.vsub(position,len),planeToFrom*len.dot(worldNormal)>0)return;if(from.distanceTo(to)=0&&d1<=1&&(from.lerp(to,d1,intersectionPoint),intersectionPoint.vsub(position,normal),normal.normalize(),this.reportIntersection(normal,intersectionPoint,reportedShape,body,-1)),this.result.shouldStop)return;d2>=0&&d2<=1&&(from.lerp(to,d2,intersectionPoint),intersectionPoint.vsub(position,normal),normal.normalize(),this.reportIntersection(normal,intersectionPoint,reportedShape,body,-1))}}_intersectConvex(shape,quat,position,body,reportedShape,options){const normal=intersectConvex_normal,vector=intersectConvex_vector,faceList=options&&options.faceList||null,faces=shape.faces,vertices=shape.vertices,normals=shape.faceNormals,direction=this.direction,from=this.from,to=this.to,fromToDistance=from.distanceTo(to),Nfaces=faceList?faceList.length:faces.length,result=this.result;for(let j=0;!result.shouldStop&&jfromToDistance||this.reportIntersection(normal,intersectPoint,reportedShape,body,fi)}}}}_intersectTrimesh(mesh,quat,position,body,reportedShape,options){const normal=intersectTrimesh_normal,triangles=intersectTrimesh_triangles,treeTransform=intersectTrimesh_treeTransform,vector=intersectConvex_vector,localDirection=intersectTrimesh_localDirection,localFrom=intersectTrimesh_localFrom,localTo=intersectTrimesh_localTo,worldIntersectPoint=intersectTrimesh_worldIntersectPoint,worldNormal=intersectTrimesh_worldNormal,indices=(options&&options.faceList,mesh.indices),from=(mesh.vertices,this.from),to=this.to,direction=this.direction;treeTransform.position.copy(position),treeTransform.quaternion.copy(quat),Transform.vectorToLocalFrame(position,quat,direction,localDirection),Transform.pointToLocalFrame(position,quat,from,localFrom),Transform.pointToLocalFrame(position,quat,to,localTo),localTo.x*=mesh.scale.x,localTo.y*=mesh.scale.y,localTo.z*=mesh.scale.z,localFrom.x*=mesh.scale.x,localFrom.y*=mesh.scale.y,localFrom.z*=mesh.scale.z,localTo.vsub(localFrom,localDirection),localDirection.normalize();const fromToDistanceSquared=localFrom.distanceSquared(localTo);mesh.tree.rayQuery(this,treeTransform,triangles);for(let i=0,N=triangles.length;!this.result.shouldStop&&i!==N;i++){const trianglesIndex=triangles[i];mesh.getNormal(trianglesIndex,normal),mesh.getVertex(indices[3*trianglesIndex],a),a.vsub(localFrom,vector);const dot=localDirection.dot(normal),scalar=normal.dot(vector)/dot;if(scalar<0)continue;localDirection.scale(scalar,intersectPoint),intersectPoint.vadd(localFrom,intersectPoint),mesh.getVertex(indices[3*trianglesIndex+1],b),mesh.getVertex(indices[3*trianglesIndex+2],c);const squaredDistance=intersectPoint.distanceSquared(localFrom);!pointInTriangle(intersectPoint,b,a,c)&&!pointInTriangle(intersectPoint,a,b,c)||squaredDistance>fromToDistanceSquared||(Transform.vectorToWorldFrame(quat,normal,worldNormal),Transform.pointToWorldFrame(position,quat,intersectPoint,worldIntersectPoint),this.reportIntersection(worldNormal,worldIntersectPoint,reportedShape,body,trianglesIndex))}triangles.length=0}reportIntersection(normal,hitPointWorld,shape,body,hitFaceIndex){const from=this.from,to=this.to,distance=from.distanceTo(hitPointWorld),result=this.result;if(!(this.skipBackfaces&&normal.dot(this.direction)>0))switch(result.hitFaceIndex=void 0!==hitFaceIndex?hitFaceIndex:-1,this.mode){case Ray.ALL:this.hasHit=!0,result.set(from,to,normal,hitPointWorld,shape,body,distance),result.hasHit=!0,this.callback(result);break;case Ray.CLOSEST:(distance=0&&(v=dot00*dot12-dot01*dot02)>=0&&u+v{axisList.push(event.body)}),this._removeBodyHandler=(event=>{const idx=axisList.indexOf(event.body);-1!==idx&&axisList.splice(idx,1)}),world&&this.setWorld(world)}setWorld(world){this.axisList.length=0;for(let i=0;ivarianceY?varianceX>varianceZ?0:2:varianceY>varianceZ?1:2}aabbQuery(world,aabb,result=[]){this.dirty&&(this.sortList(),this.dirty=!1);const axisIndex=this.axisIndex;let axis="x";1===axisIndex&&(axis="y"),2===axisIndex&&(axis="z");const axisList=this.axisList;aabb.lowerBound[axis],aabb.upperBound[axis];for(let i=0;i{for(let i=1,l=a.length;i=0&&!(a[j].aabb.lowerBound.x<=v.aabb.lowerBound.x);j--)a[j+1]=a[j];a[j+1]=v}return a}),SAPBroadphase.insertionSortY=(a=>{for(let i=1,l=a.length;i=0&&!(a[j].aabb.lowerBound.y<=v.aabb.lowerBound.y);j--)a[j+1]=a[j];a[j+1]=v}return a}),SAPBroadphase.insertionSortZ=(a=>{for(let i=1,l=a.length;i=0&&!(a[j].aabb.lowerBound.z<=v.aabb.lowerBound.z);j--)a[j+1]=a[j];a[j+1]=v}return a}),SAPBroadphase.checkBounds=((bi,bj,axisIndex)=>{let biPos,bjPos;0===axisIndex?(biPos=bi.position.x,bjPos=bj.position.x):1===axisIndex?(biPos=bi.position.y,bjPos=bj.position.y):2===axisIndex&&(biPos=bi.position.z,bjPos=bj.position.z);const ri=bi.boundingRadius;return bjPos-bj.boundingRadius{for(let key in defaults)key in options||(options[key]=defaults[key]);return options});class Constraint{constructor(bodyA,bodyB,options={}){options=Utils.defaults(options,{collideConnected:!0,wakeUpBodies:!0}),this.equations=[],this.bodyA=bodyA,this.bodyB=bodyB,this.id=Constraint.idCounter++,this.collideConnected=options.collideConnected,options.wakeUpBodies&&(bodyA&&bodyA.wakeUp(),bodyB&&bodyB.wakeUp())}update(){throw new Error("method update() not implmemented in this Constraint subclass!")}enable(){const eqs=this.equations;for(let i=0;i=-.1)this.suspensionRelativeVelocity=0,this.clippedInvContactDotSuspension=10;else{const inv=-1/project;this.suspensionRelativeVelocity=projVel*inv,this.clippedInvContactDotSuspension=inv}}else raycastResult.suspensionLength=this.suspensionRestLength,this.suspensionRelativeVelocity=0,raycastResult.directionWorld.scale(-1,raycastResult.hitNormalWorld),this.clippedInvContactDotSuspension=1}}const chassis_velocity_at_contactPoint=new Vec3,relpos=new Vec3;exports.RaycastVehicle=class{constructor(options){this.chassisBody=options.chassisBody,this.wheelInfos=[],this.sliding=!1,this.world=null,this.indexRightAxis=void 0!==options.indexRightAxis?options.indexRightAxis:1,this.indexForwardAxis=void 0!==options.indexForwardAxis?options.indexForwardAxis:0,this.indexUpAxis=void 0!==options.indexUpAxis?options.indexUpAxis:2,this.constraints=[],this.preStepCallback=(()=>{}),this.currentVehicleSpeedKmHour=0}addWheel(options={}){const info=new WheelInfo(options),index=this.wheelInfos.length;return this.wheelInfos.push(info),index}setSteeringValue(value,wheelIndex){this.wheelInfos[wheelIndex].steering=value}applyEngineForce(value,wheelIndex){this.wheelInfos[wheelIndex].engineForce=value}setBrake(brake,wheelIndex){this.wheelInfos[wheelIndex].brake=brake}addToWorld(world){this.constraints,world.addBody(this.chassisBody);const that=this;this.preStepCallback=(()=>{that.updateVehicle(world.dt)}),world.addEventListener("preStep",this.preStepCallback),this.world=world}getVehicleAxisWorld(axisIndex,result){result.set(0===axisIndex?1:0,1===axisIndex?1:0,2===axisIndex?1:0),this.chassisBody.vectorToWorldFrame(result,result)}updateVehicle(timeStep){const wheelInfos=this.wheelInfos,numWheels=wheelInfos.length,chassisBody=this.chassisBody;for(let i=0;iwheel.maxSuspensionForce&&(suspensionForce=wheel.maxSuspensionForce),wheel.raycastResult.hitNormalWorld.scale(suspensionForce*timeStep,impulse),wheel.raycastResult.hitPointWorld.vsub(chassisBody.position,relpos),chassisBody.applyImpulse(impulse,relpos)}this.updateFriction(timeStep);const hitNormalWorldScaledWithProj=new Vec3,fwd=new Vec3,vel=new Vec3;for(let i=0;i0?1:-1)*wheel.customSlidingRotationalSpeed*timeStep),Math.abs(wheel.brake)>Math.abs(wheel.engineForce)&&(wheel.deltaRotation=0),wheel.rotation+=wheel.deltaRotation,wheel.deltaRotation*=.99}}updateSuspension(deltaTime){const chassisMass=this.chassisBody.mass,wheelInfos=this.wheelInfos,numWheels=wheelInfos.length;for(let w_it=0;w_itmaxSuspensionLength&&(wheel.suspensionLength=maxSuspensionLength,wheel.raycastResult.reset());const denominator=wheel.raycastResult.hitNormalWorld.dot(wheel.directionWorld),chassis_velocity_at_contactPoint=new Vec3;chassisBody.getVelocityAtWorldPoint(wheel.raycastResult.hitPointWorld,chassis_velocity_at_contactPoint);const projVel=wheel.raycastResult.hitNormalWorld.dot(chassis_velocity_at_contactPoint);if(denominator>=-.1)wheel.suspensionRelativeVelocity=0,wheel.clippedInvContactDotSuspension=10;else{const inv=-1/denominator;wheel.suspensionRelativeVelocity=projVel*inv,wheel.clippedInvContactDotSuspension=inv}}else wheel.suspensionLength=wheel.suspensionRestLength+0*wheel.maxSuspensionTravel,wheel.suspensionRelativeVelocity=0,wheel.directionWorld.scale(-1,wheel.raycastResult.hitNormalWorld),wheel.clippedInvContactDotSuspension=1;return depth}updateWheelTransformWorld(wheel){wheel.isInContact=!1;const chassisBody=this.chassisBody;chassisBody.pointToWorldFrame(wheel.chassisConnectionPointLocal,wheel.chassisConnectionPointWorld),chassisBody.vectorToWorldFrame(wheel.directionLocal,wheel.directionWorld),chassisBody.vectorToWorldFrame(wheel.axleLocal,wheel.axleWorld)}updateWheelTransform(wheelIndex){const up=tmpVec4,right=tmpVec5,fwd=tmpVec6,wheel=this.wheelInfos[wheelIndex];this.updateWheelTransformWorld(wheel),wheel.directionLocal.scale(-1,up),right.copy(wheel.axleLocal),up.cross(right,fwd),fwd.normalize(),right.normalize();const steering=wheel.steering,steeringOrn=new Quaternion;steeringOrn.setFromAxisAngle(up,steering);const rotatingOrn=new Quaternion;rotatingOrn.setFromAxisAngle(right,wheel.rotation);const q=wheel.worldTransform.quaternion;this.chassisBody.quaternion.mult(steeringOrn,q),q.mult(rotatingOrn,q),q.normalize();const p=wheel.worldTransform.position;p.copy(wheel.directionWorld),p.scale(wheel.suspensionLength,p),p.vadd(wheel.chassisConnectionPointWorld,p)}getWheelTransformWorld(wheelIndex){return this.wheelInfos[wheelIndex].worldTransform}updateFriction(timeStep){const surfNormalWS_scaled_proj=updateFriction_surfNormalWS_scaled_proj,wheelInfos=this.wheelInfos,numWheels=wheelInfos.length,chassisBody=this.chassisBody,forwardWS=updateFriction_forwardWS,axle=updateFriction_axle;for(let i=0;imaximpSquared){this.sliding=!0,wheel.sliding=!0;const factor=maximp/Math.sqrt(impulseSquared);wheel.skidInfo*=factor}}}if(this.sliding)for(let i=0;i1.1)return 0;const vel1=resolveSingleBilateral_vel1,vel2=resolveSingleBilateral_vel2,vel=resolveSingleBilateral_vel;body1.getVelocityAtWorldPoint(pos1,vel1),body2.getVelocityAtWorldPoint(pos2,vel2),vel1.vsub(vel2,vel);return-.2*normal.dot(vel)*(1/(body1.invMass+body2.invMass))}class Sphere extends Shape{constructor(radius){if(super({type:Shape.types.SPHERE}),this.radius=void 0!==radius?radius:1,this.radius<0)throw new Error("The sphere radius cannot be negative.");this.updateBoundingSphereRadius()}calculateLocalInertia(mass,target=new Vec3){const I=2*mass*this.radius*this.radius/5;return target.x=I,target.y=I,target.z=I,target}volume(){return 4*Math.PI*Math.pow(this.radius,3)/3}updateBoundingSphereRadius(){this.boundingSphereRadius=this.radius}calculateWorldAABB(pos,quat,min,max){const r=this.radius,axes=["x","y","z"];for(let i=0;ithis.particles.length&&this.neighbors.pop())}getNeighbors(particle,neighbors){const N=this.particles.length,id=particle.id,R2=this.smoothingRadius*this.smoothingRadius,dist=SPHSystem_getNeighbors_dist;for(let i=0;i!==N;i++){const p=this.particles[i];p.position.vsub(particle.position,dist),id!==p.id&&dist.lengthSquared()maxValue&&(maxValue=v)}this.maxValue=maxValue}setHeightValueAtIndex(xi,yi,value){this.data[xi][yi]=value,this.clearCachedConvexTrianglePillar(xi,yi,!1),xi>0&&(this.clearCachedConvexTrianglePillar(xi-1,yi,!0),this.clearCachedConvexTrianglePillar(xi-1,yi,!1)),yi>0&&(this.clearCachedConvexTrianglePillar(xi,yi-1,!0),this.clearCachedConvexTrianglePillar(xi,yi-1,!1)),yi>0&&xi>0&&this.clearCachedConvexTrianglePillar(xi-1,yi-1,!0)}getRectMinMax(iMinX,iMinY,iMaxX,iMaxY,result=[]){const data=this.data;let max=this.minValue;for(let i=iMinX;i<=iMaxX;i++)for(let j=iMinY;j<=iMaxY;j++){const height=data[i][j];height>max&&(max=height)}result[0]=this.minValue,result[1]=max}getIndexOfPosition(x,y,result,clamp){const w=this.elementSize,data=this.data;let xi=Math.floor(x/w),yi=Math.floor(y/w);return result[0]=xi,result[1]=yi,clamp&&(xi<0&&(xi=0),yi<0&&(yi=0),xi>=data.length-1&&(xi=data.length-1),yi>=data[0].length-1&&(yi=data[0].length-1)),!(xi<0||yi<0||xi>=data.length-1||yi>=data[0].length-1)}getTriangleAt(x,y,edgeClamp,a,b,c){const idx=getHeightAt_idx;this.getIndexOfPosition(x,y,idx,edgeClamp);let xi=idx[0],yi=idx[1];const data=this.data;edgeClamp&&(xi=Math.min(data.length-2,Math.max(0,xi)),yi=Math.min(data[0].length-2,Math.max(0,yi)));const elementSize=this.elementSize,upper=(x/elementSize-xi)**2+(y/elementSize-yi)**2>(x/elementSize-(xi+1))**2+(y/elementSize-(yi+1))**2;return this.getTriangle(xi,yi,upper,a,b,c),upper}getNormalAt(x,y,edgeClamp,result){const a=getNormalAt_a,b=getNormalAt_b,c=getNormalAt_c,e0=getNormalAt_e0,e1=getNormalAt_e1;this.getTriangleAt(x,y,edgeClamp,a,b,c),b.vsub(a,e0),c.vsub(a,e1),e0.cross(e1,result),result.normalize()}getAabbAtIndex(xi,yi,{lowerBound:lowerBound,upperBound:upperBound}){const data=this.data,elementSize=this.elementSize;lowerBound.set(xi*elementSize,yi*elementSize,data[xi][yi]),upperBound.set((xi+1)*elementSize,(yi+1)*elementSize,data[xi+1][yi+1])}getHeightAt(x,y,edgeClamp){const data=this.data,a=getHeightAt_a,b=getHeightAt_b,c=getHeightAt_c,idx=getHeightAt_idx;this.getIndexOfPosition(x,y,idx,edgeClamp);let xi=idx[0],yi=idx[1];edgeClamp&&(xi=Math.min(data.length-2,Math.max(0,xi)),yi=Math.min(data[0].length-2,Math.max(0,yi)));const upper=this.getTriangleAt(x,y,edgeClamp,a,b,c);!function(x,y,ax,ay,bx,by,cx,cy,result){result.x=((by-cy)*(x-cx)+(cx-bx)*(y-cy))/((by-cy)*(ax-cx)+(cx-bx)*(ay-cy)),result.y=((cy-ay)*(x-cx)+(ax-cx)*(y-cy))/((by-cy)*(ax-cx)+(cx-bx)*(ay-cy)),result.z=1-result.x-result.y}(x,y,a.x,a.y,b.x,b.y,c.x,c.y,getHeightAt_weights);const w=getHeightAt_weights;return upper?data[xi+1][yi+1]*w.x+data[xi][yi+1]*w.y+data[xi+1][yi]*w.z:data[xi][yi]*w.x+data[xi+1][yi]*w.y+data[xi][yi+1]*w.z}getCacheConvexTrianglePillarKey(xi,yi,getUpperTriangle){return xi+"_"+yi+"_"+(getUpperTriangle?1:0)}getCachedConvexTrianglePillar(xi,yi,getUpperTriangle){return this._cachedPillars[this.getCacheConvexTrianglePillarKey(xi,yi,getUpperTriangle)]}setCachedConvexTrianglePillar(xi,yi,getUpperTriangle,convex,offset){this._cachedPillars[this.getCacheConvexTrianglePillarKey(xi,yi,getUpperTriangle)]={convex:convex,offset:offset}}clearCachedConvexTrianglePillar(xi,yi,getUpperTriangle){delete this._cachedPillars[this.getCacheConvexTrianglePillarKey(xi,yi,getUpperTriangle)]}getTriangle(xi,yi,upper,a,b,c){const data=this.data,elementSize=this.elementSize;upper?(a.set((xi+1)*elementSize,(yi+1)*elementSize,data[xi+1][yi+1]),b.set(xi*elementSize,(yi+1)*elementSize,data[xi][yi+1]),c.set((xi+1)*elementSize,yi*elementSize,data[xi+1][yi])):(a.set(xi*elementSize,yi*elementSize,data[xi][yi]),b.set((xi+1)*elementSize,yi*elementSize,data[xi+1][yi]),c.set(xi*elementSize,(yi+1)*elementSize,data[xi][yi+1]))}getConvexTrianglePillar(xi,yi,getUpperTriangle){let result=this.pillarConvex,offsetResult=this.pillarOffset;if(this.cacheEnabled){const data=this.getCachedConvexTrianglePillar(xi,yi,getUpperTriangle);if(data)return this.pillarConvex=data.convex,void(this.pillarOffset=data.offset);result=new ConvexPolyhedron,offsetResult=new Vec3,this.pillarConvex=result,this.pillarOffset=offsetResult}const data=this.data,elementSize=this.elementSize,faces=result.faces;result.vertices.length=6;for(let i=0;i<6;i++)result.vertices[i]||(result.vertices[i]=new Vec3);faces.length=5;for(let i=0;i<5;i++)faces[i]||(faces[i]=[]);const verts=result.vertices,h=(Math.min(data[xi][yi],data[xi+1][yi],data[xi][yi+1],data[xi+1][yi+1])-this.minValue)/2+this.minValue;getUpperTriangle?(offsetResult.set((xi+.75)*elementSize,(yi+.75)*elementSize,h),verts[0].set(.25*elementSize,.25*elementSize,data[xi+1][yi+1]-h),verts[1].set(-.75*elementSize,.25*elementSize,data[xi][yi+1]-h),verts[2].set(.25*elementSize,-.75*elementSize,data[xi+1][yi]-h),verts[3].set(.25*elementSize,.25*elementSize,-h-1),verts[4].set(-.75*elementSize,.25*elementSize,-h-1),verts[5].set(.25*elementSize,-.75*elementSize,-h-1),faces[0][0]=0,faces[0][1]=1,faces[0][2]=2,faces[1][0]=5,faces[1][1]=4,faces[1][2]=3,faces[2][0]=2,faces[2][1]=5,faces[2][2]=3,faces[2][3]=0,faces[3][0]=3,faces[3][1]=4,faces[3][2]=1,faces[3][3]=0,faces[4][0]=1,faces[4][1]=4,faces[4][2]=5,faces[4][3]=2):(offsetResult.set((xi+.25)*elementSize,(yi+.25)*elementSize,h),verts[0].set(-.25*elementSize,-.25*elementSize,data[xi][yi]-h),verts[1].set(.75*elementSize,-.25*elementSize,data[xi+1][yi]-h),verts[2].set(-.25*elementSize,.75*elementSize,data[xi][yi+1]-h),verts[3].set(-.25*elementSize,-.25*elementSize,-h-1),verts[4].set(.75*elementSize,-.25*elementSize,-h-1),verts[5].set(-.25*elementSize,.75*elementSize,-h-1),faces[0][0]=0,faces[0][1]=1,faces[0][2]=2,faces[1][0]=5,faces[1][1]=4,faces[1][2]=3,faces[2][0]=0,faces[2][1]=2,faces[2][2]=5,faces[2][3]=3,faces[3][0]=1,faces[3][1]=0,faces[3][2]=3,faces[3][3]=4,faces[4][0]=4,faces[4][1]=5,faces[4][2]=2,faces[4][3]=1),result.computeNormals(),result.computeEdges(),result.updateBoundingSphereRadius(),this.setCachedConvexTrianglePillar(xi,yi,getUpperTriangle,result,offsetResult)}calculateLocalInertia(mass,target=new Vec3){return target.set(0,0,0),target}volume(){return Number.MAX_VALUE}calculateWorldAABB(pos,quat,min,max){min.set(-Number.MAX_VALUE,-Number.MAX_VALUE,-Number.MAX_VALUE),max.set(Number.MAX_VALUE,Number.MAX_VALUE,Number.MAX_VALUE)}updateBoundingSphereRadius(){const data=this.data,s=this.elementSize;this.boundingSphereRadius=new Vec3(data.length*s,data[0].length*s,Math.max(Math.abs(this.maxValue),Math.abs(this.minValue))).length()}setHeightsFromImage(image,scale){const{x:x,z:z,y:y}=scale,canvas=document.createElement("canvas");canvas.width=image.width,canvas.height=image.height;const context=canvas.getContext("2d");context.drawImage(image,0,0);const imageData=context.getImageData(0,0,image.width,image.height),matrix=this.data;matrix.length=0,this.elementSize=Math.abs(x)/imageData.width;for(let i=0;i=0;i--)this.children[i].removeEmptyNodes(),this.children[i].children.length||this.children[i].data.length||this.children.splice(i,1)}}class Octree extends OctreeNode{constructor(aabb,options={}){super({root:null,aabb:aabb}),this.maxDepth=void 0!==options.maxDepth?options.maxDepth:8}}const halfDiagonal=new Vec3,tmpAABB$1=new AABB;class Trimesh extends Shape{constructor(vertices,indices){super({type:Shape.types.TRIMESH}),this.vertices=new Float32Array(vertices),this.indices=new Int16Array(indices),this.normals=new Float32Array(indices.length),this.aabb=new AABB,this.edges=null,this.scale=new Vec3(1,1,1),this.tree=new Octree,this.updateEdges(),this.updateNormals(),this.updateAABB(),this.updateBoundingSphereRadius(),this.updateTree()}updateTree(){const tree=this.tree;tree.reset(),tree.aabb.copy(this.aabb);const scale=this.scale;tree.aabb.lowerBound.x*=1/scale.x,tree.aabb.lowerBound.y*=1/scale.y,tree.aabb.lowerBound.z*=1/scale.z,tree.aabb.upperBound.x*=1/scale.x,tree.aabb.upperBound.y*=1/scale.y,tree.aabb.upperBound.z*=1/scale.z;const triangleAABB=new AABB,a=new Vec3,b=new Vec3,c=new Vec3,points=[a,b,c];for(let i=0;i{edges[au.x&&(u.x=v.x),v.yu.y&&(u.y=v.y),v.zu.z&&(u.z=v.z)}updateAABB(){this.computeLocalAABB(this.aabb)}updateBoundingSphereRadius(){let max2=0;const vertices=this.vertices,v=new Vec3;for(let i=0,N=vertices.length/3;i!==N;i++){this.getVertex(i,v);const norm2=v.lengthSquared();norm2>max2&&(max2=norm2)}this.boundingSphereRadius=Math.sqrt(max2)}calculateWorldAABB(pos,quat,min,max){const frame=calculateWorldAABB_frame,result=calculateWorldAABB_aabb;frame.position=pos,frame.quaternion=quat,this.aabb.toWorldFrame(frame,result),min.copy(result.lowerBound),max.copy(result.upperBound)}volume(){return 4*Math.PI*this.boundingSphereRadius/3}}exports.Trimesh=Trimesh;const computeNormals_n=new Vec3,unscaledAABB=new AABB,getEdgeVector_va=new Vec3,getEdgeVector_vb=new Vec3,cb=new Vec3,ab=new Vec3;Trimesh.computeNormal=((va,vb,vc,target)=>{vb.vsub(va,ab),vc.vsub(vb,cb),cb.cross(ab,target),target.isZero()||target.normalize()});const va=new Vec3,vb=new Vec3,vc=new Vec3,cli_aabb=new AABB,computeLocalAABB_worldVert=new Vec3,calculateWorldAABB_frame=new Transform,calculateWorldAABB_aabb=new AABB;Trimesh.createTorus=((radius=1,tube=.5,radialSegments=8,tubularSegments=6,arc=2*Math.PI)=>{const vertices=[],indices=[];for(let j=0;j<=radialSegments;j++)for(let i=0;i<=tubularSegments;i++){const u=i/tubularSegments*arc,v=j/radialSegments*Math.PI*2,x=(radius+tube*Math.cos(v))*Math.cos(u),y=(radius+tube*Math.cos(v))*Math.sin(u),z=tube*Math.sin(v);vertices.push(x,y,z)}for(let j=1;j<=radialSegments;j++)for(let i=1;i<=tubularSegments;i++){const a=(tubularSegments+1)*j+i-1,b=(tubularSegments+1)*(j-1)+i-1,c=(tubularSegments+1)*(j-1)+i,d=(tubularSegments+1)*j+i;indices.push(a,b,d),indices.push(b,c,d)}return new Trimesh(vertices,indices)});class Solver{constructor(){this.equations=[]}solve(dt,world){return 0}addEquation(eq){eq.enabled&&this.equations.push(eq)}removeEquation(eq){const eqs=this.equations,i=eqs.indexOf(eq);-1!==i&&eqs.splice(i,1)}removeAllEquations(){this.equations.length=0}}exports.Solver=Solver;class GSSolver extends Solver{constructor(){super(),this.iterations=10,this.tolerance=1e-7}solve(dt,world){let iter=0;const maxIter=this.iterations,tolSquared=this.tolerance*this.tolerance,equations=this.equations,Neq=equations.length,bodies=world.bodies,Nbodies=bodies.length,h=dt;let B,invC,deltalambda,deltalambdaTot,GWlambda,lambdaj;if(0!==Neq)for(let i=0;i!==Nbodies;i++)bodies[i].updateSolveMassProperties();const invCs=GSSolver_solve_invCs,Bs=GSSolver_solve_Bs,lambda=GSSolver_solve_lambda;invCs.length=Neq,Bs.length=Neq,lambda.length=Neq;for(let i=0;i!==Neq;i++){const c=equations[i];lambda[i]=0,Bs[i]=c.computeB(h),invCs[i]=1/c.computeC()}if(0!==Neq){for(let i=0;i!==Nbodies;i++){const b=bodies[i],vlambda=b.vlambda,wlambda=b.wlambda;vlambda.set(0,0,0),wlambda.set(0,0,0)}for(iter=0;iter!==maxIter;iter++){deltalambdaTot=0;for(let j=0;j!==Neq;j++){const c=equations[j];B=Bs[j],invC=invCs[j],(lambdaj=lambda[j])+(deltalambda=invC*(B-(GWlambda=c.computeGWlambda())-c.eps*lambdaj))c.maxForce&&(deltalambda=c.maxForce-lambdaj),lambda[j]+=deltalambda,deltalambdaTot+=deltalambda>0?deltalambda:-deltalambda,c.addToWlambda(deltalambda)}if(deltalambdaTot*deltalambdaTotsize;)objects.pop();for(;objects.length=0&&matB.restitution>=0&&(c.restitution=matA.restitution*matB.restitution),c.si=overrideShapeA||si,c.sj=overrideShapeB||sj,c}createFrictionEquationsFromContact(contactEquation,outArray){const bodyA=contactEquation.bi,bodyB=contactEquation.bj,shapeA=contactEquation.si,shapeB=contactEquation.sj,world=this.world,cm=this.currentContactMaterial;let friction=cm.friction;const matA=shapeA.material||bodyA.material,matB=shapeB.material||bodyB.material;if(matA&&matB&&matA.friction>=0&&matB.friction>=0&&(friction=matA.friction*matB.friction),friction>0){const mug=friction*world.gravity.length();let reducedMass=bodyA.invMass+bodyB.invMass;reducedMass>0&&(reducedMass=1/reducedMass);const pool=this.frictionEquationPool,c1=pool.length?pool.pop():new FrictionEquation(bodyA,bodyB,mug*reducedMass),c2=pool.length?pool.pop():new FrictionEquation(bodyA,bodyB,mug*reducedMass);return c1.bi=c2.bi=bodyA,c1.bj=c2.bj=bodyB,c1.minForce=c2.minForce=-mug*reducedMass,c1.maxForce=c2.maxForce=mug*reducedMass,c1.ri.copy(contactEquation.ri),c1.rj.copy(contactEquation.rj),c2.ri.copy(contactEquation.ri),c2.rj.copy(contactEquation.rj),contactEquation.ni.tangents(c1.t,c2.t),c1.setSpookParams(cm.frictionEquationStiffness,cm.frictionEquationRelaxation,world.dt),c2.setSpookParams(cm.frictionEquationStiffness,cm.frictionEquationRelaxation,world.dt),c1.enabled=c2.enabled=contactEquation.enabled,outArray.push(c1,c2),!0}return!1}createFrictionFromAverage(numContacts){let c=this.result[this.result.length-1];if(!this.createFrictionEquationsFromContact(c,this.frictionResult)||1===numContacts)return;const f1=this.frictionResult[this.frictionResult.length-2],f2=this.frictionResult[this.frictionResult.length-1];averageNormal.setZero(),averageContactPointA.setZero(),averageContactPointB.setZero();const bodyA=c.bi;c.bj;for(let i=0;i!==numContacts;i++)(c=this.result[this.result.length-1-i]).bi!==bodyA?(averageNormal.vadd(c.ni,averageNormal),averageContactPointA.vadd(c.ri,averageContactPointA),averageContactPointB.vadd(c.rj,averageContactPointB)):(averageNormal.vsub(c.ni,averageNormal),averageContactPointA.vadd(c.rj,averageContactPointA),averageContactPointB.vadd(c.ri,averageContactPointB));const invNumContacts=1/numContacts;averageContactPointA.scale(invNumContacts,f1.ri),averageContactPointB.scale(invNumContacts,f1.rj),f2.ri.copy(f1.ri),f2.rj.copy(f1.rj),averageNormal.normalize(),averageNormal.tangents(f1.t,f2.t)}getContacts(p1,p2,world,result,oldcontacts,frictionResult,frictionPool){this.contactPointPool=oldcontacts,this.frictionEquationPool=frictionPool,this.result=result,this.frictionResult=frictionResult;const qi=tmpQuat1,qj=tmpQuat2,xi=tmpVec1$2,xj=tmpVec2$2;for(let k=0,N=p1.length;k!==N;k++){const bi=p1[k],bj=p2[k];let bodyContactMaterial=null;bi.material&&bj.material&&(bodyContactMaterial=world.getContactMaterial(bi.material,bj.material)||null);const justTest=bi.type&Body.KINEMATIC&&bj.type&Body.STATIC||bi.type&Body.STATIC&&bj.type&Body.KINEMATIC||bi.type&Body.KINEMATIC&&bj.type&Body.KINEMATIC;for(let i=0;isi.boundingSphereRadius+sj.boundingSphereRadius)continue;let shapeContactMaterial=null;si.material&&sj.material&&(shapeContactMaterial=world.getContactMaterial(si.material,sj.material)||null),this.currentContactMaterial=shapeContactMaterial||bodyContactMaterial||world.defaultContactMaterial;const resolver=this[si.type|sj.type];if(resolver){let retval=!1;(retval=si.type0){const ns1=sphereBox_ns1,ns2=sphereBox_ns2;ns1.copy(sides[(idx+1)%3]),ns2.copy(sides[(idx+2)%3]);const h1=ns1.length(),h2=ns2.length();ns1.normalize(),ns2.normalize();const dot1=box_to_sphere.dot(ns1),dot2=box_to_sphere.dot(ns2);if(dot1-h1&&dot2-h2){const dist=Math.abs(dot-h-R);if((null===side_distance||distsi.boundingSphereRadius+sj.boundingSphereRadius)&&si.findSeparatingAxis(sj,xi,qi,xj,qj,sepAxis,faceListA,faceListB)){const res=[],q=convexConvex_q;si.clipAgainstHull(xi,qi,sj,xj,qj,sepAxis,-100,100,res);let numContacts=0;for(let j=0;j!==res.length;j++){if(justTest)return!0;const r=this.createContactEquation(bi,bj,si,sj,rsi,rsj),ri=r.ri,rj=r.rj;sepAxis.negate(r.ni),res[j].normal.negate(q),q.scale(res[j].depth,q),res[j].point.vadd(q,ri),rj.copy(res[j].point),ri.vsub(xi,ri),rj.vsub(xj,rj),ri.vadd(xi,ri),ri.vsub(bi.position,ri),rj.vadd(xj,rj),rj.vsub(bj.position,rj),this.result.push(r),numContacts++,this.enableFrictionReduction||this.createFrictionEquationsFromContact(r,this.frictionResult)}this.enableFrictionReduction&&numContacts&&this.createFrictionFromAverage(numContacts)}}sphereConvex(si,sj,xi,xj,qi,qj,bi,bj,rsi,rsj,justTest){const v3pool=this.v3pool;xi.vsub(xj,convex_to_sphere);const normals=sj.faceNormals,faces=sj.faces,verts=sj.vertices,R=si.radius;let found=!1;for(let i=0;i!==verts.length;i++){const v=verts[i],worldCorner=sphereConvex_worldCorner;qj.vmult(v,worldCorner),xj.vadd(worldCorner,worldCorner);const sphere_to_corner=sphereConvex_sphereToCorner;if(worldCorner.vsub(xi,sphere_to_corner),sphere_to_corner.lengthSquared()0){const faceVerts=[];for(let j=0,Nverts=face.length;j!==Nverts;j++){const worldVertex=v3pool.get();qj.vmult(verts[face[j]],worldVertex),xj.vadd(worldVertex,worldVertex),faceVerts.push(worldVertex)}if(pointInPolygon(faceVerts,worldNormal,xi)){if(justTest)return!0;found=!0;const r=this.createContactEquation(bi,bj,si,sj,rsi,rsj);worldNormal.scale(-R,r.ri),worldNormal.negate(r.ni);const penetrationVec2=v3pool.get();worldNormal.scale(-penetration,penetrationVec2);const penetrationSpherePoint=v3pool.get();worldNormal.scale(-R,penetrationSpherePoint),xi.vsub(xj,r.rj),r.rj.vadd(penetrationSpherePoint,r.rj),r.rj.vadd(penetrationVec2,r.rj),r.rj.vadd(xj,r.rj),r.rj.vsub(bj.position,r.rj),r.ri.vadd(xi,r.ri),r.ri.vsub(bi.position,r.ri),v3pool.release(penetrationVec2),v3pool.release(penetrationSpherePoint),this.result.push(r),this.createFrictionEquationsFromContact(r,this.frictionResult);for(let j=0,Nfaceverts=faceVerts.length;j!==Nfaceverts;j++)v3pool.release(faceVerts[j]);return}for(let j=0;j!==face.length;j++){const v1=v3pool.get(),v2=v3pool.get();qj.vmult(verts[face[(j+1)%face.length]],v1),qj.vmult(verts[face[(j+2)%face.length]],v2),xj.vadd(v1,v1),xj.vadd(v2,v2);const edge=sphereConvex_edge;v2.vsub(v1,edge);const edgeUnit=sphereConvex_edgeUnit;edge.unit(edgeUnit);const p=v3pool.get(),v1_to_xi=v3pool.get();xi.vsub(v1,v1_to_xi);const dot=v1_to_xi.dot(edgeUnit);edgeUnit.scale(dot,p),p.vadd(v1,p);const xi_to_p=v3pool.get();if(p.vsub(xi,xi_to_p),dot>0&&dot*dotdata.length||iMinY>data[0].length)return;iMinX<0&&(iMinX=0),iMaxX<0&&(iMaxX=0),iMinY<0&&(iMinY=0),iMaxY<0&&(iMaxY=0),iMinX>=data.length&&(iMinX=data.length-1),iMaxX>=data.length&&(iMaxX=data.length-1),iMaxY>=data[0].length&&(iMaxY=data[0].length-1),iMinY>=data[0].length&&(iMinY=data[0].length-1);const minMax=[];hfShape.getRectMinMax(iMinX,iMinY,iMaxX,iMaxY,minMax);const min=minMax[0],max=minMax[1];if(localSpherePos.z-radius>max||localSpherePos.z+radius2)return}}boxHeightfield(si,sj,xi,xj,qi,qj,bi,bj,rsi,rsj,justTest){return si.convexPolyhedronRepresentation.material=si.material,si.convexPolyhedronRepresentation.collisionResponse=si.collisionResponse,this.convexHeightfield(si.convexPolyhedronRepresentation,sj,xi,xj,qi,qj,bi,bj,si,sj,justTest)}convexHeightfield(convexShape,hfShape,convexPos,hfPos,convexQuat,hfQuat,convexBody,hfBody,rsi,rsj,justTest){const data=hfShape.data,w=hfShape.elementSize,radius=convexShape.boundingSphereRadius,worldPillarOffset=convexHeightfield_tmp2,faceList=convexHeightfield_faceList,localConvexPos=convexHeightfield_tmp1;Transform.pointToLocalFrame(hfPos,hfQuat,convexPos,localConvexPos);let iMinX=Math.floor((localConvexPos.x-radius)/w)-1,iMaxX=Math.ceil((localConvexPos.x+radius)/w)+1,iMinY=Math.floor((localConvexPos.y-radius)/w)-1,iMaxY=Math.ceil((localConvexPos.y+radius)/w)+1;if(iMaxX<0||iMaxY<0||iMinX>data.length||iMinY>data[0].length)return;iMinX<0&&(iMinX=0),iMaxX<0&&(iMaxX=0),iMinY<0&&(iMinY=0),iMaxY<0&&(iMaxY=0),iMinX>=data.length&&(iMinX=data.length-1),iMaxX>=data.length&&(iMaxX=data.length-1),iMaxY>=data[0].length&&(iMaxY=data[0].length-1),iMinY>=data[0].length&&(iMinY=data[0].length-1);const minMax=[];hfShape.getRectMinMax(iMinX,iMinY,iMaxX,iMaxY,minMax);const min=minMax[0],max=minMax[1];if(!(localConvexPos.z-radius>max||localConvexPos.z+radius0&&positionAlongEdgeB<0){if(localSpherePos.vsub(edgeVertexA,tmp),edgeVectorUnit.copy(edgeVector),edgeVectorUnit.normalize(),positionAlongEdgeA=tmp.dot(edgeVectorUnit),edgeVectorUnit.scale(positionAlongEdgeA,tmp),tmp.vadd(edgeVertexA,tmp),tmp.distanceTo(localSpherePos)0&&!0===positiveResult||r<=0&&!1===positiveResult))return!1;null===positiveResult&&(positiveResult=r>0)}return!0}const box_to_sphere=new Vec3,sphereBox_ns=new Vec3,sphereBox_ns1=new Vec3,sphereBox_ns2=new Vec3,sphereBox_sides=[new Vec3,new Vec3,new Vec3,new Vec3,new Vec3,new Vec3],sphereBox_sphere_to_corner=new Vec3,sphereBox_side_ns=new Vec3,sphereBox_side_ns1=new Vec3,sphereBox_side_ns2=new Vec3;Narrowphase.prototype[COLLISION_TYPES.sphereBox]=Narrowphase.prototype.sphereBox;const convex_to_sphere=new Vec3,sphereConvex_edge=new Vec3,sphereConvex_edgeUnit=new Vec3,sphereConvex_sphereToCorner=new Vec3,sphereConvex_worldCorner=new Vec3,sphereConvex_worldNormal=new Vec3,sphereConvex_worldPoint=new Vec3,sphereConvex_worldSpherePointClosestToPlane=new Vec3,sphereConvex_penetrationVec=new Vec3,sphereConvex_sphereToWorldPoint=new Vec3;Narrowphase.prototype[COLLISION_TYPES.sphereConvex]=Narrowphase.prototype.sphereConvex,Narrowphase.prototype[COLLISION_TYPES.planeBox]=Narrowphase.prototype.planeBox;const planeConvex_v=new Vec3,planeConvex_normal=new Vec3,planeConvex_relpos=new Vec3,planeConvex_projected=new Vec3;Narrowphase.prototype[COLLISION_TYPES.planeConvex]=Narrowphase.prototype.planeConvex;const convexConvex_sepAxis=new Vec3,convexConvex_q=new Vec3;Narrowphase.prototype[COLLISION_TYPES.convexConvex]=Narrowphase.prototype.convexConvex;const particlePlane_normal=new Vec3,particlePlane_relpos=new Vec3,particlePlane_projected=new Vec3;Narrowphase.prototype[COLLISION_TYPES.planeParticle]=Narrowphase.prototype.planeParticle;const particleSphere_normal=new Vec3;Narrowphase.prototype[COLLISION_TYPES.sphereParticle]=Narrowphase.prototype.sphereParticle;const cqj=new Quaternion,convexParticle_local=new Vec3,convexParticle_penetratedFaceNormal=new Vec3,convexParticle_vertexToParticle=new Vec3,convexParticle_worldPenetrationVec=new Vec3;Narrowphase.prototype[COLLISION_TYPES.convexParticle]=Narrowphase.prototype.convexParticle,Narrowphase.prototype[COLLISION_TYPES.boxHeightfield]=Narrowphase.prototype.boxHeightfield;const convexHeightfield_tmp1=new Vec3,convexHeightfield_tmp2=new Vec3,convexHeightfield_faceList=[0];Narrowphase.prototype[COLLISION_TYPES.convexHeightfield]=Narrowphase.prototype.convexHeightfield;const sphereHeightfield_tmp1=new Vec3,sphereHeightfield_tmp2=new Vec3;Narrowphase.prototype[COLLISION_TYPES.sphereHeightfield]=Narrowphase.prototype.sphereHeightfield;class OverlapKeeper{constructor(){this.current=[],this.previous=[]}getKey(i,j){if(jcurrent[index];)index++;if(key!==current[index]){for(let j=current.length-1;j>=index;j--)current[j+1]=current[j];current[index]=key}}tick(){const tmp=this.current;this.current=this.previous,this.previous=tmp,this.current.length=0}getDiff(additions,removals){const a=this.current,b=this.previous,al=a.length,bl=b.length;let j=0;for(let i=0;ib[j];)j++;(found=keyA===b[j])||unpackAndPush(additions,keyA)}j=0;for(let i=0;ia[j];)j++;(found=a[j]===keyB)||unpackAndPush(removals,keyB)}}}function unpackAndPush(array,key){array.push((4294901760&key)>>16,65535&key)}class TupleDictionary{constructor(){this.data={keys:[]}}get(i,j){if(i>j){const temp=j;j=i,i=temp}return this.data[i+"-"+j]}set(i,j,value){if(i>j){const temp=j;j=i,i=temp}const key=i+"-"+j;this.get(i,j)||this.data.keys.push(key),this.data[key]=value}reset(){const data=this.data,keys=data.keys;for(;keys.length>0;){delete data[keys.pop()]}}}class World extends EventTarget{constructor(options={}){super(),this.dt=-1,this.allowSleep=!!options.allowSleep,this.contacts=[],this.frictionEquations=[],this.quatNormalizeSkip=void 0!==options.quatNormalizeSkip?options.quatNormalizeSkip:0,this.quatNormalizeFast=void 0!==options.quatNormalizeFast&&options.quatNormalizeFast,this.time=0,this.stepnumber=0,this.default_dt=1/60,this.nextId=0,this.gravity=new Vec3,options.gravity&&this.gravity.copy(options.gravity),this.broadphase=void 0!==options.broadphase?options.broadphase:new NaiveBroadphase,this.bodies=[],this.hasActiveBodies=!1,this.solver=void 0!==options.solver?options.solver:new GSSolver,this.constraints=[],this.narrowphase=new Narrowphase(this),this.collisionMatrix=new ArrayCollisionMatrix,this.collisionMatrixPrevious=new ArrayCollisionMatrix,this.bodyOverlapKeeper=new OverlapKeeper,this.shapeOverlapKeeper=new OverlapKeeper,this.materials=[],this.contactmaterials=[],this.contactMaterialTable=new TupleDictionary,this.defaultMaterial=new Material("default"),this.defaultContactMaterial=new ContactMaterial(this.defaultMaterial,this.defaultMaterial,{friction:.3,restitution:0}),this.doProfiling=!1,this.profile={solve:0,makeContactConstraints:0,broadphase:0,integrate:0,narrowphase:0},this.accumulator=0,this.subsystems=[],this.addBodyEvent={type:"addBody",body:null},this.removeBodyEvent={type:"removeBody",body:null},this.idToBodyMap={},this.broadphase.setWorld(this)}getContactMaterial(m1,m2){return this.contactMaterialTable.get(m1.id,m2.id)}numObjects(){return this.bodies.length}collisionMatrixTick(){const temp=this.collisionMatrixPrevious;this.collisionMatrixPrevious=this.collisionMatrix,this.collisionMatrix=temp,this.collisionMatrix.reset(),this.bodyOverlapKeeper.tick(),this.shapeOverlapKeeper.tick()}addConstraint(c){this.constraints.push(c)}removeConstraint(c){const idx=this.constraints.indexOf(c);-1!==idx&&this.constraints.splice(idx,1)}rayTest(from,to,result){result instanceof RaycastResult?this.raycastClosest(from,to,{skipBackfaces:!0},result):this.raycastAll(from,to,{skipBackfaces:!0},result)}raycastAll(from,to,options={},callback){return options.mode=Ray.ALL,options.from=from,options.to=to,options.callback=callback,tmpRay$1.intersectWorld(this,options)}raycastAny(from,to,options={},result){return options.mode=Ray.ANY,options.from=from,options.to=to,options.result=result,tmpRay$1.intersectWorld(this,options)}raycastClosest(from,to,options={},result){return options.mode=Ray.CLOSEST,options.from=from,options.to=to,options.result=result,tmpRay$1.intersectWorld(this,options)}addBody(body){this.bodies.includes(body)||(body.index=this.bodies.length,this.bodies.push(body),body.world=this,body.initPosition.copy(body.position),body.initVelocity.copy(body.velocity),body.timeLastSleepy=this.time,body instanceof Body&&(body.initAngularVelocity.copy(body.angularVelocity),body.initQuaternion.copy(body.quaternion)),this.collisionMatrix.setNumObjects(this.bodies.length),this.addBodyEvent.body=body,this.idToBodyMap[body.id]=body,this.dispatchEvent(this.addBodyEvent))}removeBody(body){body.world=null;const n=this.bodies.length-1,bodies=this.bodies,idx=bodies.indexOf(body);if(-1!==idx){bodies.splice(idx,1);for(let i=0;i!==bodies.length;i++)bodies[i].index=i;this.collisionMatrix.setNumObjects(n),this.removeBodyEvent.body=body,delete this.idToBodyMap[body.id],this.dispatchEvent(this.removeBodyEvent)}}getBodyById(id){return this.idToBodyMap[id]}getShapeById(id){const bodies=this.bodies;for(let i=0,bl=bodies.length;i=dt&&substeps=0;j-=1)(c.bodyA===p1[j]&&c.bodyB===p2[j]||c.bodyB===p1[j]&&c.bodyA===p2[j])&&(p1.splice(j,1),p2.splice(j,1))}this.collisionMatrixTick(),doProfiling&&(profilingStart=performance.now());const oldcontacts=World_step_oldContacts,NoldContacts=contacts.length;for(i=0;i!==NoldContacts;i++)oldcontacts.push(contacts[i]);contacts.length=0;const NoldFrictionEquations=this.frictionEquations.length;for(i=0;i!==NoldFrictionEquations;i++)frictionEquationPool.push(this.frictionEquations[i]);for(this.frictionEquations.length=0,this.narrowphase.getContacts(p1,p2,this,contacts,oldcontacts,this.frictionEquations,frictionEquationPool),doProfiling&&(profile.narrowphase=performance.now()-profilingStart),doProfiling&&(profilingStart=performance.now()),i=0;i=0&&bj.material.friction>=0&&(mu=bi.material.friction*bj.material.friction),bi.material.restitution>=0&&bj.material.restitution>=0&&(c.restitution=bi.material.restitution*bj.material.restitution)),solver.addEquation(c),bi.allowSleep&&bi.type===Body.DYNAMIC&&bi.sleepState===Body.SLEEPING&&bj.sleepState===Body.AWAKE&&bj.type!==Body.STATIC){bj.velocity.lengthSquared()+bj.angularVelocity.lengthSquared()>=2*bj.sleepSpeedLimit**2&&(bi.wakeUpAfterNarrowphase=!0)}if(bj.allowSleep&&bj.type===Body.DYNAMIC&&bj.sleepState===Body.SLEEPING&&bi.sleepState===Body.AWAKE&&bi.type!==Body.STATIC){bi.velocity.lengthSquared()+bi.angularVelocity.lengthSquared()>=2*bi.sleepSpeedLimit**2&&(bj.wakeUpAfterNarrowphase=!0)}this.collisionMatrix.set(bi,bj,!0),this.collisionMatrixPrevious.get(bi,bj)||(World_step_collideEvent.body=bj,World_step_collideEvent.contact=c,bi.dispatchEvent(World_step_collideEvent),World_step_collideEvent.body=bi,bj.dispatchEvent(World_step_collideEvent)),this.bodyOverlapKeeper.set(bi.id,bj.id),this.shapeOverlapKeeper.set(si.id,sj.id)}for(this.emitContactEvents(),doProfiling&&(profile.makeContactConstraints=performance.now()-profilingStart,profilingStart=performance.now()),i=0;i!==N;i++){const bi=bodies[i];bi.wakeUpAfterNarrowphase&&(bi.wakeUp(),bi.wakeUpAfterNarrowphase=!1)}for(Nconstraints=constraints.length,i=0;i!==Nconstraints;i++){const c=constraints[i];c.update();for(let j=0,Neq=c.equations.length;j!==Neq;j++){const eq=c.equations[j];solver.addEquation(eq)}}solver.solve(dt,this),doProfiling&&(profile.solve=performance.now()-profilingStart),solver.removeAllEquations();const pow=Math.pow;for(i=0;i!==N;i++){const bi=bodies[i];if(bi.type&DYNAMIC){const ld=pow(1-bi.linearDamping,dt),v=bi.velocity;v.scale(ld,v);const av=bi.angularVelocity;if(av){const ad=pow(1-bi.angularDamping,dt);av.scale(ad,av)}}}for(this.dispatchEvent(World_step_preStepEvent),i=0;i!==N;i++){const bi=bodies[i];bi.preStep&&bi.preStep.call(bi)}doProfiling&&(profilingStart=performance.now());const quatNormalize=this.stepnumber%(this.quatNormalizeSkip+1)==0,quatNormalizeFast=this.quatNormalizeFast;for(i=0;i!==N;i++)bodies[i].integrate(dt,quatNormalize,quatNormalizeFast);for(this.clearForces(),this.broadphase.dirty=!0,doProfiling&&(profile.integrate=performance.now()-profilingStart),this.time+=dt,this.stepnumber+=1,this.dispatchEvent(World_step_postStepEvent),i=0;i!==N;i++){const bi=bodies[i],postStep=bi.postStep;postStep&&postStep.call(bi)}let hasActiveBodies=!0;if(this.allowSleep)for(hasActiveBodies=!1,i=0;i!==N;i++){const bi=bodies[i];bi.sleepTick(this.time),bi.sleepState!==Body.SLEEPING&&(hasActiveBodies=!0)}this.hasActiveBodies=hasActiveBodies}clearForces(){const bodies=this.bodies,N=bodies.length;for(let i=0;i!==N;i++){const b=bodies[i];b.force,b.torque;b.force.set(0,0,0),b.torque.set(0,0,0)}}}exports.World=World;new AABB;const tmpRay$1=new Ray;if("undefined"==typeof performance&&(performance={}),!performance.now){let nowOffset=Date.now();performance.timing&&performance.timing.navigationStart&&(nowOffset=performance.timing.navigationStart),performance.now=(()=>Date.now()-nowOffset)}const World_step_postStepEvent={type:"postStep"},World_step_preStepEvent={type:"preStep"},World_step_collideEvent={type:Body.COLLIDE_EVENT_NAME,body:null,contact:null},World_step_oldContacts=[],World_step_frictionEquationPool=[],World_step_p1=[],World_step_p2=[];World.prototype.emitContactEvents=(()=>{const additions=[],removals=[],beginContactEvent={type:"beginContact",bodyA:null,bodyB:null},endContactEvent={type:"endContact",bodyA:null,bodyB:null},beginShapeContactEvent={type:"beginShapeContact",bodyA:null,bodyB:null,shapeA:null,shapeB:null},endShapeContactEvent={type:"endShapeContact",bodyA:null,bodyB:null,shapeA:null,shapeB:null};return function(){const hasBeginContact=this.hasAnyEventListener("beginContact"),hasEndContact=this.hasAnyEventListener("endContact");if((hasBeginContact||hasEndContact)&&this.bodyOverlapKeeper.getDiff(additions,removals),hasBeginContact){for(let i=0,l=additions.length;i{switch(options.cylinderAxis){case"y":return new Ammo.btCylinderShape(btHalfExtents);case"x":return new Ammo.btCylinderShapeX(btHalfExtents);case"z":return new Ammo.btCylinderShapeZ(btHalfExtents)}return null})();return Ammo.destroy(btHalfExtents),_finishCollisionShape(collisionShape,options,_computeScale(matrixWorld,options)),collisionShape};exports.createCylinderShape=createCylinderShape;const createCapsuleShape=function(vertices,matrices,matrixWorld,options={}){options.type=TYPE.CAPSULE,_setOptions(options),options.fit===FIT.ALL&&(options.halfExtents=_computeHalfExtents(_computeBounds(vertices,matrices),options.minHalfExtent,options.maxHalfExtent));const{x:x,y:y,z:z}=options.halfExtents,collisionShape=(()=>{switch(options.cylinderAxis){case"y":return new Ammo.btCapsuleShape(Math.max(x,z),2*y);case"x":return new Ammo.btCapsuleShapeX(Math.max(y,z),2*x);case"z":return new Ammo.btCapsuleShapeZ(Math.max(x,y),2*z)}return null})();return _finishCollisionShape(collisionShape,options,_computeScale(matrixWorld,options)),collisionShape};exports.createCapsuleShape=createCapsuleShape;const createConeShape=function(vertices,matrices,matrixWorld,options={}){options.type=TYPE.CONE,_setOptions(options),options.fit===FIT.ALL&&(options.halfExtents=_computeHalfExtents(_computeBounds(vertices,matrices),options.minHalfExtent,options.maxHalfExtent));const{x:x,y:y,z:z}=options.halfExtents,collisionShape=(()=>{switch(options.cylinderAxis){case"y":return new Ammo.btConeShape(Math.max(x,z),2*y);case"x":return new Ammo.btConeShapeX(Math.max(y,z),2*x);case"z":return new Ammo.btConeShapeZ(Math.max(x,y),2*z)}return null})();return _finishCollisionShape(collisionShape,options,_computeScale(matrixWorld,options)),collisionShape};exports.createConeShape=createConeShape;const createSphereShape=function(vertices,matrices,matrixWorld,options={}){let radius;options.type=TYPE.SPHERE,_setOptions(options),radius=options.fit!==FIT.MANUAL||isNaN(options.sphereRadius)?_computeRadius(vertices,matrices,_computeBounds(vertices,matrices)):options.sphereRadius;const collisionShape=new Ammo.btSphereShape(radius);return _finishCollisionShape(collisionShape,options,_computeScale(matrixWorld,options)),collisionShape};exports.createSphereShape=createSphereShape;const createHullShape=exports.createHullShape=function(){const vertex=new THREE.Vector3,center=new THREE.Vector3,matrix=new THREE.Matrix4;return function(vertices,matrices,matrixWorld,options={}){if(options.type=TYPE.HULL,_setOptions(options),options.fit===FIT.MANUAL)return console.warn("cannot use fit: manual with type: hull"),null;const bounds=_computeBounds(vertices,matrices),btVertex=new Ammo.btVector3,originalHull=new Ammo.btConvexHullShape;originalHull.setMargin(options.margin),center.addVectors(bounds.max,bounds.min).multiplyScalar(.5);let vertexCount=0;for(let i=0;imaxVertices&&console.warn(`too many vertices for hull shape; sampling ~${maxVertices} from ~${vertexCount} vertices`);const p=Math.min(1,maxVertices/vertexCount);for(let i=0;i=100){const shapeHull=new Ammo.btShapeHull(originalHull);shapeHull.buildHull(options.margin),Ammo.destroy(originalHull),collisionShape=new Ammo.btConvexHullShape(Ammo.getPointer(shapeHull.getVertexPointer()),shapeHull.numVertices()),Ammo.destroy(shapeHull)}return Ammo.destroy(btVertex),_finishCollisionShape(collisionShape,options,_computeScale(matrixWorld,options)),collisionShape}}(),createHACDShapes=exports.createHACDShapes=function(){const vector=new THREE.Vector3,center=new THREE.Vector3,matrix=new THREE.Matrix4;return function(vertices,matrices,indexes,matrixWorld,options={}){if(options.type=TYPE.HACD,_setOptions(options),options.fit===FIT.MANUAL)return console.warn("cannot use fit: manual with type: hacd"),[];if(!Ammo.hasOwnProperty("HACD"))return console.warn("HACD unavailable in included build of Ammo.js. Visit https://github.com/mozillareality/ammo.js for the latest version."),[];const bounds=_computeBounds(vertices,matrices),scale=_computeScale(matrixWorld,options);let vertexCount=0,triCount=0;center.addVectors(bounds.max,bounds.min).multiplyScalar(.5);for(let i=0;i{switch(options.heightDataType){case"short":return Ammo.PHY_SHORT;case"float":default:return Ammo.PHY_FLOAT}})(),flipQuadEdges=!options.hasOwnProperty("flipQuadEdges")||options.flipQuadEdges,heightStickLength=heightfieldData.length,heightStickWidth=heightStickLength>0?heightfieldData[0].length:0,data=Ammo._malloc(heightStickLength*heightStickWidth*4),ptr=data/4;let minHeight=Number.POSITIVE_INFINITY,maxHeight=Number.NEGATIVE_INFINITY,index=0;for(let l=0;l{for(let res of collisionShape.resources||[])Ammo.destroy(res);collisionShape.heightfieldData&&Ammo._free(collisionShape.heightfieldData),Ammo.destroy(collisionShape)});const localTransform=new Ammo.btTransform,rotation=new Ammo.btQuaternion;if(localTransform.setIdentity(),localTransform.getOrigin().setValue(options.offset.x,options.offset.y,options.offset.z),rotation.setValue(options.orientation.x,options.orientation.y,options.orientation.z,options.orientation.w),localTransform.setRotation(rotation),Ammo.destroy(rotation),scale){const localScale=new Ammo.btVector3(scale.x,scale.y,scale.z);collisionShape.setLocalScaling(localScale),Ammo.destroy(localScale)}collisionShape.localTransform=localTransform},_computeScale=(exports.iterateGeometries=function(){const inverse=new THREE.Matrix4;return function(root,options,cb){inverse.copy(root.matrixWorld).invert(),(new THREE.Vector3).setFromMatrixScale(root.matrixWorld),root.traverse(mesh=>{const transform=new THREE.Matrix4;mesh.isMesh&&"Sky"!==mesh.name&&(options.includeInvisible||mesh.el&&mesh.el.object3D.visible||mesh.visible)&&(mesh===root?transform.identity():(mesh.updateWorldMatrix(!0),transform.multiplyMatrices(inverse,mesh.matrixWorld)),cb(mesh.geometry.isBufferGeometry?mesh.geometry.attributes.position.array:mesh.geometry.vertices,transform.elements,mesh.geometry.index?mesh.geometry.index.array:null))})}}(),function(){const matrix=new THREE.Matrix4;return function(matrixWorld,options={}){const scale=new THREE.Vector3(1,1,1);return options.fit===FIT.ALL&&(matrix.fromArray(matrixWorld),scale.setFromMatrixScale(matrix)),scale}}()),_computeRadius=function(){const center=new THREE.Vector3;return function(vertices,matrices,bounds){let maxRadiusSq=0,{x:cx,y:cy,z:cz}=bounds.getCenter(center);return _iterateVertices(vertices,matrices,v=>{const dx=cx-v.x,dy=cy-v.y,dz=cz-v.z;maxRadiusSq=Math.max(maxRadiusSq,dx*dx+dy*dy+dz*dz)}),Math.sqrt(maxRadiusSq)}}(),_computeHalfExtents=function(bounds,minHalfExtent,maxHalfExtent){return(new THREE.Vector3).subVectors(bounds.max,bounds.min).multiplyScalar(.5).clampScalar(minHalfExtent,maxHalfExtent)},_computeBounds=function(vertices,matrices){const bounds=new THREE.Box3;let minX=1/0,minY=1/0,minZ=1/0,maxX=-1/0,maxY=-1/0,maxZ=-1/0;return bounds.min.set(0,0,0),bounds.max.set(0,0,0),_iterateVertices(vertices,matrices,v=>{v.xmaxX&&(maxX=v.x),v.y>maxY&&(maxY=v.y),v.z>maxZ&&(maxZ=v.z)}),bounds.min.set(minX,minY,minZ),bounds.max.set(maxX,maxY,maxZ),bounds},_iterateVertices=function(){const vertex=new THREE.Vector3,matrix=new THREE.Matrix4;return function(vertices,matrices,cb){for(let i=0;ithis.tolerance)return!1}return!0},intersectRay:function(ray,target){for(var faces=this.faces,tNear=-1/0,tFar=1/0,i=0,l=faces.length;i0&&vD>=0)return null;var t=0!==vD?-vN/vD:0;if(!(t<=0)&&(vD>0?tFar=Math.min(t,tFar):tNear=Math.max(t,tNear),tNear>tFar))return null}return tNear!==-1/0?ray.at(tNear,target):ray.at(tFar,target),target},intersectsRay:function(ray){return null!==this.intersectRay(ray,v1)},makeEmpty:function(){return this.faces=[],this.vertices=[],this},addVertexToFace:function(vertex,face){return vertex.face=face,null===face.outside?this.assigned.append(vertex):this.assigned.insertBefore(face.outside,vertex),face.outside=vertex,this},removeVertexFromFace:function(vertex,face){return vertex===face.outside&&(null!==vertex.next&&vertex.next.face===face?face.outside=vertex.next:face.outside=null),this.assigned.remove(vertex),this},removeAllVerticesFromFace:function(face){if(null!==face.outside){for(var start=face.outside,end=face.outside;null!==end.next&&end.next.face===face;)end=end.next;return this.assigned.removeSubList(start,end),start.prev=end.next=null,face.outside=null,start}},deleteFaceVertices:function(face,absorbingFace){var faceVertices=this.removeAllVerticesFromFace(face);if(void 0!==faceVertices)if(void 0===absorbingFace)this.unassigned.appendChain(faceVertices);else{var vertex=faceVertices;do{var nextVertex=vertex.next;absorbingFace.distanceToPoint(vertex.point)>this.tolerance?this.addVertexToFace(vertex,absorbingFace):this.unassigned.append(vertex),vertex=nextVertex}while(null!==vertex)}return this},resolveUnassignedPoints:function(newFaces){if(!1===this.unassigned.isEmpty()){var vertex=this.unassigned.first();do{for(var nextVertex=vertex.next,maxDistance=this.tolerance,maxFace=null,i=0;imaxDistance&&(maxDistance=distance,maxFace=face),maxDistance>1e3*this.tolerance)break}}null!==maxFace&&this.addVertexToFace(vertex,maxFace),vertex=nextVertex}while(null!==vertex)}return this},computeExtremes:function(){var i,l,j,min=new _three.Vector3,max=new _three.Vector3,minVertices=[],maxVertices=[];for(i=0;i<3;i++)minVertices[i]=maxVertices[i]=this.vertices[0];for(min.copy(this.vertices[0].point),max.copy(this.vertices[0].point),i=0,l=this.vertices.length;imax.getComponent(j)&&(max.setComponent(j,point.getComponent(j)),maxVertices[j]=vertex)}return this.tolerance=3*Number.EPSILON*(Math.max(Math.abs(min.x),Math.abs(max.x))+Math.max(Math.abs(min.y),Math.abs(max.y))+Math.max(Math.abs(min.z),Math.abs(max.z))),{min:minVertices,max:maxVertices}},computeInitialHull:function(){void 0===line3&&(line3=new _three.Line3,plane=new _three.Plane,closestPoint=new _three.Vector3);var vertex,v0,v1,v2,v3,i,l,j,distance,vertices=this.vertices,extremes=this.computeExtremes(),min=extremes.min,max=extremes.max,maxDistance=0,index=0;for(i=0;i<3;i++)(distance=max[i].point.getComponent(i)-min[i].point.getComponent(i))>maxDistance&&(maxDistance=distance,index=i);for(v0=min[index],v1=max[index],maxDistance=0,line3.set(v0.point,v1.point),i=0,l=this.vertices.length;imaxDistance&&(maxDistance=distance,v2=vertex));for(maxDistance=-1,plane.setFromCoplanarPoints(v0.point,v1.point,v2.point),i=0,l=this.vertices.length;imaxDistance&&(maxDistance=distance,v3=vertex);var faces=[];if(plane.distanceToPoint(v3.point)<0)for(faces.push(Face.create(v0,v1,v2),Face.create(v3,v1,v0),Face.create(v3,v2,v1),Face.create(v3,v0,v2)),i=0;i<3;i++)j=(i+1)%3,faces[i+1].getEdge(2).setTwin(faces[0].getEdge(j)),faces[i+1].getEdge(1).setTwin(faces[j+1].getEdge(0));else for(faces.push(Face.create(v0,v2,v1),Face.create(v3,v0,v1),Face.create(v3,v1,v2),Face.create(v3,v2,v0)),i=0;i<3;i++)j=(i+1)%3,faces[i+1].getEdge(2).setTwin(faces[0].getEdge((3-i)%3)),faces[i+1].getEdge(0).setTwin(faces[j+1].getEdge(1));for(i=0;i<4;i++)this.faces.push(faces[i]);for(i=0,l=vertices.length;imaxDistance&&(maxDistance=distance,maxFace=this.faces[j]);null!==maxFace&&this.addVertexToFace(vertex,maxFace)}return this},reindexFaces:function(){for(var activeFaces=[],i=0;imaxDistance&&(maxDistance=distance,eyeVertex=vertex),vertex=vertex.next}while(null!==vertex&&vertex.face===eyeFace);return eyeVertex}},computeHorizon:function(eyePoint,crossEdge,face,horizon){var edge;this.deleteFaceVertices(face),face.mark=1,edge=null===crossEdge?crossEdge=face.getEdge(0):crossEdge.next;do{var twinEdge=edge.twin,oppositeFace=twinEdge.face;oppositeFace.mark===Visible&&(oppositeFace.distanceToPoint(eyePoint)>this.tolerance?this.computeHorizon(eyePoint,twinEdge,oppositeFace,horizon):horizon.push(edge)),edge=edge.next}while(edge!==crossEdge);return this},addAdjoiningFace:function(eyeVertex,horizonEdge){var face=Face.create(eyeVertex,horizonEdge.tail(),horizonEdge.head());return this.faces.push(face),face.getEdge(-1).setTwin(horizonEdge.twin),face.getEdge(0)},addNewFaces:function(eyeVertex,horizon){this.newFaces=[];for(var firstSideEdge=null,previousSideEdge=null,i=0;i0;)edge=edge.next,i--;for(;i<0;)edge=edge.prev,i++;return edge},compute:function(){void 0===triangle&&(triangle=new _three.Triangle);var a=this.edge.tail(),b=this.edge.head(),c=this.edge.next.head();return triangle.set(a.point,b.point,c.point),triangle.getNormal(this.normal),triangle.getMidpoint(this.midpoint),this.area=triangle.getArea(),this.constant=this.normal.dot(this.midpoint),this},distanceToPoint:function(point){return this.normal.dot(point)-this.constant}}),Object.assign(HalfEdge.prototype,{head:function(){return this.vertex},tail:function(){return this.prev?this.prev.vertex:null},length:function(){var head=this.head(),tail=this.tail();return null!==tail?tail.point.distanceTo(head.point):-1},lengthSquared:function(){var head=this.head(),tail=this.tail();return null!==tail?tail.point.distanceToSquared(head.point):-1},setTwin:function(edge){return this.twin=edge,edge.twin=this,this}}),Object.assign(VertexList.prototype,{first:function(){return this.head},last:function(){return this.tail},clear:function(){return this.head=this.tail=null,this},insertBefore:function(target,vertex){return vertex.prev=target.prev,vertex.next=target,null===vertex.prev?this.head=vertex:vertex.prev.next=vertex,target.prev=vertex,this},insertAfter:function(target,vertex){return vertex.prev=target,vertex.next=target.next,null===vertex.next?this.tail=vertex:vertex.next.prev=vertex,target.next=vertex,this},append:function(vertex){return null===this.head?this.head=vertex:this.tail.next=vertex,vertex.prev=this.tail,vertex.next=null,this.tail=vertex,this},appendChain:function(vertex){for(null===this.head?this.head=vertex:this.tail.next=vertex,vertex.prev=this.tail;null!==vertex.next;)vertex=vertex.next;return this.tail=vertex,this},remove:function(vertex){return null===vertex.prev?this.head=vertex.next:vertex.prev.next=vertex.next,null===vertex.next?this.tail=vertex.prev:vertex.next.prev=vertex.prev,this},removeSubList:function(a,b){return null===a.prev?this.head=b.next:a.prev.next=b.next,null===b.next?this.tail=a.prev:b.next.prev=a.prev,this},isEmpty:function(){return null===this.head}}),ConvexHull}();const _v1=new _three.Vector3,_v2=new _three.Vector3,_q1=new _three.Quaternion;function getGeometry(object){const meshes=function(object){const meshes=[];return object.traverse(function(o){o.isMesh&&meshes.push(o)}),meshes}(object);if(0===meshes.length)return null;if(1===meshes.length)return normalizeGeometry(meshes[0]);let mesh;const geometries=[];for(;mesh=meshes.pop();)geometries.push(simplifyGeometry(normalizeGeometry(mesh)));return function(geometries){let vertexCount=0;for(let i=0;i{this.loadedEventFired=!0},{once:!0}),this.system.initialized&&this.loadedEventFired&&this.initBody()},initBody:function(){const pos=new THREE.Vector3,quat=new THREE.Quaternion;new THREE.Box3;return function(){const el=this.el,data=this.data;this.localScaling=new Ammo.btVector3;const obj=this.el.object3D;obj.getWorldPosition(pos),obj.getWorldQuaternion(quat),this.prevScale=new THREE.Vector3(1,1,1),this.prevNumChildShapes=0,this.msTransform=new Ammo.btTransform,this.msTransform.setIdentity(),this.rotation=new Ammo.btQuaternion(quat.x,quat.y,quat.z,quat.w),this.msTransform.getOrigin().setValue(pos.x,pos.y,pos.z),this.msTransform.setRotation(this.rotation),this.motionState=new Ammo.btDefaultMotionState(this.msTransform),this.localInertia=new Ammo.btVector3(0,0,0),this.compoundShape=new Ammo.btCompoundShape(!0),this.rbInfo=new Ammo.btRigidBodyConstructionInfo(data.mass,this.motionState,this.compoundShape,this.localInertia),this.rbInfo.m_restitution=((num,min,max)=>Math.min(Math.max(num,min),max))(this.data.restitution,0,1),this.body=new Ammo.btRigidBody(this.rbInfo),this.body.setActivationState(ACTIVATION_STATES.indexOf(data.activationState)+1),this.body.setSleepingThresholds(data.linearSleepingThreshold,data.angularSleepingThreshold),this.body.setDamping(data.linearDamping,data.angularDamping);const angularFactor=new Ammo.btVector3(data.angularFactor.x,data.angularFactor.y,data.angularFactor.z);this.body.setAngularFactor(angularFactor),Ammo.destroy(angularFactor),this._updateBodyGravity(data.gravity),this.updateCollisionFlags(),this.el.body=this.body,this.body.el=el,this.isLoaded=!0,this.el.emit("body-loaded",{body:this.el.body}),this._addToSystem()}}(),tick:function(){this.system.initialized&&!this.isLoaded&&this.loadedEventFired&&this.initBody()},_updateBodyGravity(gravity){if(void 0!==gravity.x&&void 0!==gravity.y&&void 0!==gravity.z){const gravityBtVec=new Ammo.btVector3(gravity.x,gravity.y,gravity.z);epsilon=.001,u=gravityBtVec,v=this.system.driver.physicsWorld.getGravity(),Math.abs(u.x()-v.x()){vertices.push(vertexArray),matrices.push(matrixArray),indexes.push(indexArray)});const collisionShapes=threeToAmmo.createCollisionShapes(vertices,matrices,indexes,matrixWorld.elements,data);shapeComponent.addShapes(collisionShapes)},play:function(){this.isLoaded&&this._addToSystem()},_addToSystem:function(){this.addedToSystem||(this.system.addBody(this.body,this.data.collisionFilterGroup,this.data.collisionFilterMask),this.data.emitCollisionEvents&&this.system.driver.addEventListener(this.body),this.system.addComponent(this),this.addedToSystem=!0)},pause:function(){this.addedToSystem&&(this.system.removeComponent(this),this.system.removeBody(this.body),this.addedToSystem=!1)},update:function(prevData){if(this.isLoaded){if(!this.hasUpdated)return void(this.hasUpdated=!0);const data=this.data;if(prevData.type===data.type&&prevData.disableCollision===data.disableCollision||this.updateCollisionFlags(),prevData.activationState!==data.activationState&&(this.body.forceActivationState(ACTIVATION_STATES.indexOf(data.activationState)+1),data.activationState===ACTIVATION_STATE.ACTIVE_TAG&&this.body.activate(!0)),prevData.collisionFilterGroup!==data.collisionFilterGroup||prevData.collisionFilterMask!==data.collisionFilterMask){const broadphaseProxy=this.body.getBroadphaseProxy();broadphaseProxy.set_m_collisionFilterGroup(data.collisionFilterGroup),broadphaseProxy.set_m_collisionFilterMask(data.collisionFilterMask),this.system.driver.broadphase.getOverlappingPairCache().removeOverlappingPairsContainingProxy(broadphaseProxy,this.system.driver.dispatcher)}if(prevData.linearDamping==data.linearDamping&&prevData.angularDamping==data.angularDamping||this.body.setDamping(data.linearDamping,data.angularDamping),almostEqualsVector3(.001,prevData.gravity,data.gravity)||this._updateBodyGravity(data.gravity),prevData.linearSleepingThreshold==data.linearSleepingThreshold&&prevData.angularSleepingThreshold==data.angularSleepingThreshold||this.body.setSleepingThresholds(data.linearSleepingThreshold,data.angularSleepingThreshold),!almostEqualsVector3(.001,prevData.angularFactor,data.angularFactor)){const angularFactor=new Ammo.btVector3(data.angularFactor.x,data.angularFactor.y,data.angularFactor.z);this.body.setAngularFactor(angularFactor),Ammo.destroy(angularFactor)}prevData.restitution!=data.restitution&&console.warn("ammo-body restitution cannot be updated from its initial value.")}},remove:function(){this.triMesh&&Ammo.destroy(this.triMesh),this.localScaling&&Ammo.destroy(this.localScaling),this.compoundShape&&Ammo.destroy(this.compoundShape),this.body&&(Ammo.destroy(this.body),delete this.body),Ammo.destroy(this.rbInfo),Ammo.destroy(this.msTransform),Ammo.destroy(this.motionState),Ammo.destroy(this.localInertia),Ammo.destroy(this.rotation)},beforeStep:function(){this._updateShapes(),this.data.type===TYPE.KINEMATIC&&this.syncToPhysics()},step:function(){this.data.type===TYPE.DYNAMIC&&this.syncFromPhysics()},syncToPhysics:function(){const q=new THREE.Quaternion,v=new THREE.Vector3,q2=new THREE.Vector3,v2=new THREE.Vector3;return function(){const el=this.el,parentEl=el.parentEl;if(!this.body)return;this.motionState.getWorldTransform(this.msTransform),parentEl.isScene?(v.copy(el.object3D.position),q.copy(el.object3D.quaternion)):(el.object3D.getWorldPosition(v),el.object3D.getWorldQuaternion(q));const position=this.msTransform.getOrigin();v2.set(position.x(),position.y(),position.z());const quaternion=this.msTransform.getRotation();q2.set(quaternion.x(),quaternion.y(),quaternion.z(),quaternion.w()),almostEqualsVector3(.001,v,v2)&&function(epsilon,u,v){return Math.abs(u.x-v.x)0;){const collisionShape=this.collisionShapes.pop();collisionShape.destroy(),Ammo.destroy(collisionShape.localTransform)}}};module.exports.definition=AmmoShape,module.exports.Component=AFRAME.registerComponent("ammo-shape",AmmoShape)},{"../../constants":20,"three-to-ammo":6}],18:[function(require,module,exports){var CANNON=require("cannon-es"),Shape={schema:{shape:{default:"box",oneOf:["box","sphere","cylinder"]},offset:{type:"vec3",default:{x:0,y:0,z:0}},orientation:{type:"vec4",default:{x:0,y:0,z:0,w:1}},radius:{type:"number",default:1,if:{shape:["sphere"]}},halfExtents:{type:"vec3",default:{x:.5,y:.5,z:.5},if:{shape:["box"]}},radiusTop:{type:"number",default:1,if:{shape:["cylinder"]}},radiusBottom:{type:"number",default:1,if:{shape:["cylinder"]}},height:{type:"number",default:1,if:{shape:["cylinder"]}},numSegments:{type:"int",default:8,if:{shape:["cylinder"]}}},multiple:!0,init:function(){this.el.sceneEl.hasLoaded?this.initShape():this.el.sceneEl.addEventListener("loaded",this.initShape.bind(this))},initShape:function(){this.bodyEl=this.el;for(var bodyType=this._findType(this.bodyEl),data=this.data;!bodyType&&this.bodyEl.parentNode!=this.el.sceneEl;)this.bodyEl=this.bodyEl.parentNode,bodyType=this._findType(this.bodyEl);if(bodyType){var shape,offset,orientation,scale=new THREE.Vector3;switch(this.bodyEl.object3D.getWorldScale(scale),data.hasOwnProperty("offset")&&(offset=new CANNON.Vec3(data.offset.x*scale.x,data.offset.y*scale.y,data.offset.z*scale.z)),data.hasOwnProperty("orientation")&&(orientation=new CANNON.Quaternion).copy(data.orientation),data.shape){case"sphere":shape=new CANNON.Sphere(data.radius*scale.x);break;case"box":var halfExtents=new CANNON.Vec3(data.halfExtents.x*scale.x,data.halfExtents.y*scale.y,data.halfExtents.z*scale.z);shape=new CANNON.Box(halfExtents);break;case"cylinder":shape=new CANNON.Cylinder(data.radiusTop*scale.x,data.radiusBottom*scale.x,data.height*scale.y,data.numSegments);var quat=new CANNON.Quaternion;quat.setFromEuler(90*THREE.MathUtils.DEG2RAD,0,0,"XYZ").normalize(),orientation.mult(quat,orientation);break;default:return void console.warn(data.shape+" shape not supported")}this.bodyEl.body?this.bodyEl.components[bodyType].addShape(shape,offset,orientation):this.bodyEl.addEventListener("body-loaded",function(){this.bodyEl.components[bodyType].addShape(shape,offset,orientation)},{once:!0})}else console.warn("body not found")},_findType:function(el){return el.hasAttribute("body")?"body":el.hasAttribute("dynamic-body")?"dynamic-body":el.hasAttribute("static-body")?"static-body":null},remove:function(){this.bodyEl.parentNode&&console.warn("removing shape component not currently supported")}};module.exports.definition=Shape,module.exports.Component=AFRAME.registerComponent("shape",Shape)},{"cannon-es":5}],19:[function(require,module,exports){var CANNON=require("cannon-es");module.exports=AFRAME.registerComponent("spring",{multiple:!0,schema:{target:{type:"selector"},restLength:{default:1,min:0},stiffness:{default:100,min:0},damping:{default:1,min:0},localAnchorA:{type:"vec3",default:{x:0,y:0,z:0}},localAnchorB:{type:"vec3",default:{x:0,y:0,z:0}}},init:function(){this.system=this.el.sceneEl.systems.physics,this.system.addComponent(this),this.isActive=!0,this.spring=null},update:function(oldData){var el=this.el,data=this.data;data.target?el.body&&data.target.body?(this.createSpring(),this.updateSpring(oldData)):(el.body?data.target:el).addEventListener("body-loaded",this.update.bind(this,{})):console.warn("Spring: invalid target specified.")},updateSpring:function(oldData){if(this.spring){var data=this.data,spring=this.spring;Object.keys(data).forEach(function(attr){if(data[attr]!==oldData[attr]){if("target"===attr)return void(spring.bodyB=data.target.body);spring[attr]=data[attr]}})}else console.warn("Spring: Component attempted to change spring before its created. No changes made.")},createSpring:function(){this.spring||(this.spring=new CANNON.Spring(this.el.body))},step:function(t,dt){return this.spring&&this.isActive?this.spring.applyForce():void 0},play:function(){this.isActive=!0},pause:function(){this.isActive=!1},remove:function(){this.spring&&delete this.spring,this.spring=null}})},{"cannon-es":5}],20:[function(require,module,exports){module.exports={GRAVITY:-9.8,MAX_INTERVAL:4/60,ITERATIONS:10,CONTACT_MATERIAL:{friction:.01,restitution:.3,contactEquationStiffness:1e8,contactEquationRelaxation:3,frictionEquationStiffness:1e8,frictionEquationRegularization:3},ACTIVATION_STATE:{ACTIVE_TAG:"active",ISLAND_SLEEPING:"islandSleeping",WANTS_DEACTIVATION:"wantsDeactivation",DISABLE_DEACTIVATION:"disableDeactivation",DISABLE_SIMULATION:"disableSimulation"},COLLISION_FLAG:{STATIC_OBJECT:1,KINEMATIC_OBJECT:2,NO_CONTACT_RESPONSE:4,CUSTOM_MATERIAL_CALLBACK:8,CHARACTER_OBJECT:16,DISABLE_VISUALIZE_OBJECT:32,DISABLE_SPU_COLLISION_PROCESSING:64},TYPE:{STATIC:"static",DYNAMIC:"dynamic",KINEMATIC:"kinematic"},SHAPE:{BOX:"box",CYLINDER:"cylinder",SPHERE:"sphere",CAPSULE:"capsule",CONE:"cone",HULL:"hull",HACD:"hacd",VHACD:"vhacd",MESH:"mesh",HEIGHTFIELD:"heightfield"},FIT:{ALL:"all",MANUAL:"manual"},CONSTRAINT:{LOCK:"lock",FIXED:"fixed",SPRING:"spring",SLIDER:"slider",HINGE:"hinge",CONE_TWIST:"coneTwist",POINT_TO_POINT:"pointToPoint"}}},{}],21:[function(require,module,exports){const Driver=require("./driver");"undefined"!=typeof window&&(window.AmmoModule=window.Ammo,window.Ammo=null);function AmmoDriver(){this.collisionConfiguration=null,this.dispatcher=null,this.broadphase=null,this.solver=null,this.physicsWorld=null,this.debugDrawer=null,this.els=new Map,this.eventListeners=[],this.collisions=new Map,this.collisionKeys=[],this.currentCollisions=new Map}AmmoDriver.prototype=new Driver,AmmoDriver.prototype.constructor=AmmoDriver,module.exports=AmmoDriver,AmmoDriver.prototype.init=function(worldConfig){return new Promise(resolve=>{AmmoModule().then(result=>{Ammo=result,this.epsilon=worldConfig.epsilon||1e-5,this.debugDrawMode=worldConfig.debugDrawMode||THREE.AmmoDebugConstants.NoDebug,this.maxSubSteps=worldConfig.maxSubSteps||4,this.fixedTimeStep=worldConfig.fixedTimeStep||1/60,this.collisionConfiguration=new Ammo.btDefaultCollisionConfiguration,this.dispatcher=new Ammo.btCollisionDispatcher(this.collisionConfiguration),this.broadphase=new Ammo.btDbvtBroadphase,this.solver=new Ammo.btSequentialImpulseConstraintSolver,this.physicsWorld=new Ammo.btDiscreteDynamicsWorld(this.dispatcher,this.broadphase,this.solver,this.collisionConfiguration),this.physicsWorld.setForceUpdateAllAabbs(!1),this.physicsWorld.setGravity(new Ammo.btVector3(0,worldConfig.hasOwnProperty("gravity")?worldConfig.gravity:-9.8,0)),this.physicsWorld.getSolverInfo().set_m_numIterations(worldConfig.solverIterations),resolve()})})},AmmoDriver.prototype.addBody=function(body,group,mask){this.physicsWorld.addRigidBody(body,group,mask);const bodyptr=Ammo.getPointer(body);this.els.set(bodyptr,body.el),this.collisions.set(bodyptr,[]),this.collisionKeys.push(bodyptr),this.currentCollisions.set(bodyptr,new Set)},AmmoDriver.prototype.removeBody=function(body){this.physicsWorld.removeRigidBody(body),this.removeEventListener(body);const bodyptr=Ammo.getPointer(body);this.els.delete(bodyptr),this.collisions.delete(bodyptr),this.collisionKeys.splice(this.collisionKeys.indexOf(bodyptr),1),this.currentCollisions.delete(bodyptr)},AmmoDriver.prototype.updateBody=function(body){this.els.has(Ammo.getPointer(body))&&this.physicsWorld.updateSingleAabb(body)},AmmoDriver.prototype.step=function(deltaTime){this.physicsWorld.stepSimulation(deltaTime,this.maxSubSteps,this.fixedTimeStep);const numManifolds=this.dispatcher.getNumManifolds();for(let i=0;i=0;j--){const body1ptr=body1ptrs[j];this.currentCollisions.get(body0ptr).has(body1ptr)||(-1!==this.eventListeners.indexOf(body0ptr)&&this.els.get(body0ptr).emit("collideend",{targetEl:this.els.get(body1ptr)}),-1!==this.eventListeners.indexOf(body1ptr)&&this.els.get(body1ptr).emit("collideend",{targetEl:this.els.get(body0ptr)}),body1ptrs.splice(j,1))}this.currentCollisions.get(body0ptr).clear()}this.debugDrawer&&this.debugDrawer.update()},AmmoDriver.prototype.addConstraint=function(constraint){this.physicsWorld.addConstraint(constraint,!1)},AmmoDriver.prototype.removeConstraint=function(constraint){this.physicsWorld.removeConstraint(constraint)},AmmoDriver.prototype.addEventListener=function(body){this.eventListeners.push(Ammo.getPointer(body))},AmmoDriver.prototype.removeEventListener=function(body){const ptr=Ammo.getPointer(body);-1!==this.eventListeners.indexOf(ptr)&&this.eventListeners.splice(this.eventListeners.indexOf(ptr),1)},AmmoDriver.prototype.destroy=function(){Ammo.destroy(this.collisionConfiguration),Ammo.destroy(this.dispatcher),Ammo.destroy(this.broadphase),Ammo.destroy(this.solver),Ammo.destroy(this.physicsWorld),Ammo.destroy(this.debugDrawer)},AmmoDriver.prototype.getDebugDrawer=function(scene,options){return this.debugDrawer||((options=options||{}).debugDrawMode=options.debugDrawMode||this.debugDrawMode,this.debugDrawer=new THREE.AmmoDebugDrawer(scene,this.physicsWorld,options)),this.debugDrawer}},{"./driver":22}],22:[function(require,module,exports){function Driver(){}function abstractMethod(){throw new Error("Method not implemented.")}module.exports=Driver,Driver.prototype.init=abstractMethod,Driver.prototype.step=abstractMethod,Driver.prototype.destroy=abstractMethod,Driver.prototype.addBody=abstractMethod,Driver.prototype.removeBody=abstractMethod,Driver.prototype.applyBodyMethod=abstractMethod,Driver.prototype.updateBodyProperties=abstractMethod,Driver.prototype.addMaterial=abstractMethod,Driver.prototype.addContactMaterial=abstractMethod,Driver.prototype.addConstraint=abstractMethod,Driver.prototype.removeConstraint=abstractMethod,Driver.prototype.getContacts=abstractMethod},{}],23:[function(require,module,exports){module.exports={INIT:"init",STEP:"step",ADD_BODY:"add-body",REMOVE_BODY:"remove-body",APPLY_BODY_METHOD:"apply-body-method",UPDATE_BODY_PROPERTIES:"update-body-properties",ADD_MATERIAL:"add-material",ADD_CONTACT_MATERIAL:"add-contact-material",ADD_CONSTRAINT:"add-constraint",REMOVE_CONSTRAINT:"remove-constraint",COLLIDE:"collide"}},{}],24:[function(require,module,exports){var CANNON=require("cannon-es"),Driver=require("./driver");function LocalDriver(){this.world=null,this.materials={},this.contactMaterial=null}LocalDriver.prototype=new Driver,LocalDriver.prototype.constructor=LocalDriver,module.exports=LocalDriver,LocalDriver.prototype.init=function(worldConfig){var world=new CANNON.World;world.quatNormalizeSkip=worldConfig.quatNormalizeSkip,world.quatNormalizeFast=worldConfig.quatNormalizeFast,world.solver.iterations=worldConfig.solverIterations,world.gravity.set(0,worldConfig.gravity,0),world.broadphase=new CANNON.NaiveBroadphase,this.world=world},LocalDriver.prototype.step=function(deltaMS){this.world.step(deltaMS)},LocalDriver.prototype.destroy=function(){delete this.world,delete this.contactMaterial,this.materials={}},LocalDriver.prototype.addBody=function(body){this.world.addBody(body)},LocalDriver.prototype.removeBody=function(body){this.world.removeBody(body)},LocalDriver.prototype.applyBodyMethod=function(body,methodName,args){body["__"+methodName].apply(body,args)},LocalDriver.prototype.updateBodyProperties=function(){},LocalDriver.prototype.getMaterial=function(name){return this.materials[name]},LocalDriver.prototype.addMaterial=function(materialConfig){this.materials[materialConfig.name]=new CANNON.Material(materialConfig),this.materials[materialConfig.name].name=materialConfig.name},LocalDriver.prototype.addContactMaterial=function(matName1,matName2,contactMaterialConfig){var mat1=this.materials[matName1],mat2=this.materials[matName2];this.contactMaterial=new CANNON.ContactMaterial(mat1,mat2,contactMaterialConfig),this.world.addContactMaterial(this.contactMaterial)},LocalDriver.prototype.addConstraint=function(constraint){constraint.type||(constraint instanceof CANNON.LockConstraint?constraint.type="LockConstraint":constraint instanceof CANNON.DistanceConstraint?constraint.type="DistanceConstraint":constraint instanceof CANNON.HingeConstraint?constraint.type="HingeConstraint":constraint instanceof CANNON.ConeTwistConstraint?constraint.type="ConeTwistConstraint":constraint instanceof CANNON.PointToPointConstraint&&(constraint.type="PointToPointConstraint")),this.world.addConstraint(constraint)},LocalDriver.prototype.removeConstraint=function(constraint){this.world.removeConstraint(constraint)},LocalDriver.prototype.getContacts=function(){return this.world.contacts}},{"./driver":22,"cannon-es":5}],25:[function(require,module,exports){var Driver=require("./driver");function NetworkDriver(){throw new Error("[NetworkDriver] Driver not implemented.")}NetworkDriver.prototype=new Driver,NetworkDriver.prototype.constructor=NetworkDriver,module.exports=NetworkDriver},{"./driver":22}],26:[function(require,module,exports){function EventTarget(){this.listeners=[]}module.exports=function(worker){var targetA=new EventTarget,targetB=new EventTarget;return targetA.setTarget(targetB),targetB.setTarget(targetA),worker(targetA),targetB},EventTarget.prototype.setTarget=function(target){this.target=target},EventTarget.prototype.addEventListener=function(type,fn){this.listeners.push(fn)},EventTarget.prototype.dispatchEvent=function(type,event){for(var i=0;ithis.frameDelay;)this.frameBuffer.shift(),prevFrame=this.frameBuffer[0],nextFrame=this.frameBuffer[1];if(prevFrame&&nextFrame){var mix=(timestamp-prevFrame.timestamp)/this.frameDelay;for(var id in mix=(mix-(1-1/this.interpBufferSize))*this.interpBufferSize,prevFrame.bodies)prevFrame.bodies.hasOwnProperty(id)&&nextFrame.bodies.hasOwnProperty(id)&&protocol.deserializeInterpBodyUpdate(prevFrame.bodies[id],nextFrame.bodies[id],this.bodies[id],mix)}}},WorkerDriver.prototype.destroy=function(){this.worker.terminate(),delete this.worker},WorkerDriver.prototype._onMessage=function(event){if(event.data.type===Event.STEP){var bodies=event.data.bodies;if(this.contacts=event.data.contacts,this.interpolate)this.frameBuffer.push({timestamp:performance.now(),bodies:bodies});else for(var id in bodies)bodies.hasOwnProperty(id)&&protocol.deserializeBodyUpdate(bodies[id],this.bodies[id])}else{if(event.data.type!==Event.COLLIDE)throw new Error("[WorkerDriver] Unexpected message type.");var body=this.bodies[event.data.bodyID],target=this.bodies[event.data.targetID],contact=protocol.deserializeContact(event.data.contact,this.bodies);if(!body._listeners||!body._listeners.collide)return;for(var i=0;ithis.countBodiesAmmo(),local:()=>this.countBodiesCannon(!1),worker:()=>this.countBodiesCannon(!0)},this.bodyTypeToStatsPropertyMap={ammo:{[TYPE.STATIC]:"staticBodies",[TYPE.KINEMATIC]:"kinematicBodies",[TYPE.DYNAMIC]:"dynamicBodies"},cannon:{[CANNON.Body.STATIC]:"staticBodies",[CANNON.Body.DYNAMIC]:"dynamicBodies"}},this.el.sceneEl.setAttribute("stats-collector","inEvent: physics-tick-data;\n properties: before, after, engine, total;\n outputFrequency: 100;\n outEvent: physics-tick-summary;\n outputs: percentile__50, percentile__90, max")}if(this.statsToPanel){const scene=this.el.sceneEl,space="   ";scene.setAttribute("stats-panel",""),scene.setAttribute("stats-group__bodies","label: Physics Bodies"),scene.setAttribute("stats-row__b1","group: bodies;\n event:physics-body-data;\n properties: staticBodies;\n label: Static"),scene.setAttribute("stats-row__b2","group: bodies;\n event:physics-body-data;\n properties: dynamicBodies;\n label: Dynamic"),"local"===this.data.driver||"worker"===this.data.driver?scene.setAttribute("stats-row__b3","group: bodies;\n event:physics-body-data;\n properties: contacts;\n label: Contacts"):"ammo"===this.data.driver&&(scene.setAttribute("stats-row__b3","group: bodies;\n event:physics-body-data;\n properties: kinematicBodies;\n label: Kinematic"),scene.setAttribute("stats-row__b4","group: bodies;\n event: physics-body-data;\n properties: manifolds;\n label: Manifolds"),scene.setAttribute("stats-row__b5","group: bodies;\n event: physics-body-data;\n properties: manifoldContacts;\n label: Contacts"),scene.setAttribute("stats-row__b6","group: bodies;\n event: physics-body-data;\n properties: collisions;\n label: Collisions"),scene.setAttribute("stats-row__b7","group: bodies;\n event: physics-body-data;\n properties: collisionKeys;\n label: Coll Keys")),scene.setAttribute("stats-group__tick",`label: Physics Ticks: Median${space}90th%${space}99th%`),scene.setAttribute("stats-row__1","group: tick;\n event:physics-tick-summary;\n properties: before.percentile__50, \n before.percentile__90, \n before.max;\n label: Before"),scene.setAttribute("stats-row__2","group: tick;\n event:physics-tick-summary;\n properties: after.percentile__50, \n after.percentile__90, \n after.max; \n label: After"),scene.setAttribute("stats-row__3","group: tick; \n event:physics-tick-summary; \n properties: engine.percentile__50, \n engine.percentile__90, \n engine.max;\n label: Engine"),scene.setAttribute("stats-row__4","group: tick;\n event:physics-tick-summary;\n properties: total.percentile__50, \n total.percentile__90, \n total.max;\n label: Total")}},tick:function(t,dt){if(!this.initialized||!dt)return;const beforeStartTime=performance.now();var i,callbacks=this.callbacks;for(i=0;i{const property=this.bodyTypeToStatsPropertyMap.ammo[(el=el,el.components["ammo-body"].data.type)];statsData[property]++})},countBodiesCannon(worker){const statsData=this.statsBodyData;statsData.contacts=worker?this.driver.contacts.length:this.driver.world.contacts.length,statsData.staticBodies=0,statsData.dynamicBodies=0,(worker?Object.values(this.driver.bodies):this.driver.world.bodies).forEach(body=>{const property=this.bodyTypeToStatsPropertyMap.cannon[body.type];statsData[property]++})},setDebug:function(debug){this.debug=debug,"ammo"===this.data.driver&&this.initialized&&(debug&&!this.debugDrawer?(this.debugDrawer=this.driver.getDebugDrawer(this.el.object3D),this.debugDrawer.enable()):this.debugDrawer&&(this.debugDrawer.disable(),this.debugDrawer=null))},addBody:function(body,group,mask){var driver=this.driver;"local"===this.data.driver&&(body.__applyImpulse=body.applyImpulse,body.applyImpulse=function(){driver.applyBodyMethod(body,"applyImpulse",arguments)},body.__applyForce=body.applyForce,body.applyForce=function(){driver.applyBodyMethod(body,"applyForce",arguments)},body.updateProperties=function(){driver.updateBodyProperties(body)},this.listeners[body.id]=function(e){body.el.emit("collide",e)},body.addEventListener("collide",this.listeners[body.id])),this.driver.addBody(body,group,mask)},removeBody:function(body){this.driver.removeBody(body),"local"!==this.data.driver&&"worker"!==this.data.driver||(body.removeEventListener("collide",this.listeners[body.id]),delete this.listeners[body.id],body.applyImpulse=body.__applyImpulse,delete body.__applyImpulse,body.applyForce=body.__applyForce,delete body.__applyForce,delete body.updateProperties)},addConstraint:function(constraint){this.driver.addConstraint(constraint)},removeConstraint:function(constraint){this.driver.removeConstraint(constraint)},addComponent:function(component){var callbacks=this.callbacks;component.beforeStep&&callbacks.beforeStep.push(component),component.step&&callbacks.step.push(component),component.afterStep&&callbacks.afterStep.push(component)},removeComponent:function(component){var callbacks=this.callbacks;component.beforeStep&&callbacks.beforeStep.splice(callbacks.beforeStep.indexOf(component),1),component.step&&callbacks.step.splice(callbacks.step.indexOf(component),1),component.afterStep&&callbacks.afterStep.splice(callbacks.afterStep.indexOf(component),1)},getContacts:function(){return this.driver.getContacts()},getMaterial:function(name){return this.driver.getMaterial(name)}})},{"./constants":20,"./drivers/ammo-driver":21,"./drivers/local-driver":24,"./drivers/network-driver":25,"./drivers/worker-driver":27,"aframe-stats-panel":3,"cannon-es":5}],30:[function(require,module,exports){module.exports.slerp=function(a,b,t){if(t<=0)return a;if(t>=1)return b;var x=a[0],y=a[1],z=a[2],w=a[3],cosHalfTheta=w*b[3]+x*b[0]+y*b[1]+z*b[2];if(!(cosHalfTheta<0))return b;if((a=a.slice())[3]=-b[3],a[0]=-b[0],a[1]=-b[1],a[2]=-b[2],(cosHalfTheta=-cosHalfTheta)>=1)return a[3]=w,a[0]=x,a[1]=y,a[2]=z,this;var sinHalfTheta=Math.sqrt(1-cosHalfTheta*cosHalfTheta);if(Math.abs(sinHalfTheta)<.001)return a[3]=.5*(w+a[3]),a[0]=.5*(x+a[0]),a[1]=.5*(y+a[1]),a[2]=.5*(z+a[2]),this;var halfTheta=Math.atan2(sinHalfTheta,cosHalfTheta),ratioA=Math.sin((1-t)*halfTheta)/sinHalfTheta,ratioB=Math.sin(t*halfTheta)/sinHalfTheta;return a[3]=w*ratioA+a[3]*ratioB,a[0]=x*ratioA+a[0]*ratioB,a[1]=y*ratioA+a[1]*ratioB,a[2]=z*ratioA+a[2]*ratioB,a}},{}],31:[function(require,module,exports){var CANNON=require("cannon-es"),mathUtils=require("./math"),ID="__id";module.exports.ID=ID;var nextID={};function serializeShape(shape){var shapeMsg={type:shape.type};if(shape.type===CANNON.Shape.types.BOX)shapeMsg.halfExtents=serializeVec3(shape.halfExtents);else if(shape.type===CANNON.Shape.types.SPHERE)shapeMsg.radius=shape.radius;else{if(shape._type!==CANNON.Shape.types.CYLINDER)throw new Error("Unimplemented shape type: %s",shape.type);shapeMsg.type=CANNON.Shape.types.CYLINDER,shapeMsg.radiusTop=shape.radiusTop,shapeMsg.radiusBottom=shape.radiusBottom,shapeMsg.height=shape.height,shapeMsg.numSegments=shape.numSegments}return shapeMsg}function deserializeShape(message){var shape;if(message.type===CANNON.Shape.types.BOX)shape=new CANNON.Box(deserializeVec3(message.halfExtents));else if(message.type===CANNON.Shape.types.SPHERE)shape=new CANNON.Sphere(message.radius);else{if(message.type!==CANNON.Shape.types.CYLINDER)throw new Error("Unimplemented shape type: %s",message.type);(shape=new CANNON.Cylinder(message.radiusTop,message.radiusBottom,message.height,message.numSegments))._type=CANNON.Shape.types.CYLINDER}return shape}function serializeVec3(vec3){return vec3.toArray()}function deserializeVec3(message){return new CANNON.Vec3(message[0],message[1],message[2])}function serializeQuaternion(quat){return quat.toArray()}function deserializeQuaternion(message){return new CANNON.Quaternion(message[0],message[1],message[2],message[3])}module.exports.assignID=function(prefix,object){object[ID]||(nextID[prefix]=nextID[prefix]||1,object[ID]=prefix+"_"+nextID[prefix]++)},module.exports.serializeBody=function(body){return{shapes:body.shapes.map(serializeShape),shapeOffsets:body.shapeOffsets.map(serializeVec3),shapeOrientations:body.shapeOrientations.map(serializeQuaternion),position:serializeVec3(body.position),quaternion:body.quaternion.toArray(),velocity:serializeVec3(body.velocity),angularVelocity:serializeVec3(body.angularVelocity),id:body[ID],mass:body.mass,linearDamping:body.linearDamping,angularDamping:body.angularDamping,fixedRotation:body.fixedRotation,allowSleep:body.allowSleep,sleepSpeedLimit:body.sleepSpeedLimit,sleepTimeLimit:body.sleepTimeLimit}},module.exports.deserializeBodyUpdate=function(message,body){return body.position.set(message.position[0],message.position[1],message.position[2]),body.quaternion.set(message.quaternion[0],message.quaternion[1],message.quaternion[2],message.quaternion[3]),body.velocity.set(message.velocity[0],message.velocity[1],message.velocity[2]),body.angularVelocity.set(message.angularVelocity[0],message.angularVelocity[1],message.angularVelocity[2]),body.linearDamping=message.linearDamping,body.angularDamping=message.angularDamping,body.fixedRotation=message.fixedRotation,body.allowSleep=message.allowSleep,body.sleepSpeedLimit=message.sleepSpeedLimit,body.sleepTimeLimit=message.sleepTimeLimit,body.mass!==message.mass&&(body.mass=message.mass,body.updateMassProperties()),body},module.exports.deserializeInterpBodyUpdate=function(message1,message2,body,mix){var weight1=1-mix,weight2=mix;body.position.set(message1.position[0]*weight1+message2.position[0]*weight2,message1.position[1]*weight1+message2.position[1]*weight2,message1.position[2]*weight1+message2.position[2]*weight2);var quaternion=mathUtils.slerp(message1.quaternion,message2.quaternion,mix);return body.quaternion.set(quaternion[0],quaternion[1],quaternion[2],quaternion[3]),body.velocity.set(message1.velocity[0]*weight1+message2.velocity[0]*weight2,message1.velocity[1]*weight1+message2.velocity[1]*weight2,message1.velocity[2]*weight1+message2.velocity[2]*weight2),body.angularVelocity.set(message1.angularVelocity[0]*weight1+message2.angularVelocity[0]*weight2,message1.angularVelocity[1]*weight1+message2.angularVelocity[1]*weight2,message1.angularVelocity[2]*weight1+message2.angularVelocity[2]*weight2),body.linearDamping=message2.linearDamping,body.angularDamping=message2.angularDamping,body.fixedRotation=message2.fixedRotation,body.allowSleep=message2.allowSleep,body.sleepSpeedLimit=message2.sleepSpeedLimit,body.sleepTimeLimit=message2.sleepTimeLimit,body.mass!==message2.mass&&(body.mass=message2.mass,body.updateMassProperties()),body},module.exports.deserializeBody=function(message){for(var shapeMsg,body=new CANNON.Body({mass:message.mass,position:deserializeVec3(message.position),quaternion:deserializeQuaternion(message.quaternion),velocity:deserializeVec3(message.velocity),angularVelocity:deserializeVec3(message.angularVelocity),linearDamping:message.linearDamping,angularDamping:message.angularDamping,fixedRotation:message.fixedRotation,allowSleep:message.allowSleep,sleepSpeedLimit:message.sleepSpeedLimit,sleepTimeLimit:message.sleepTimeLimit}),i=0;shapeMsg=message.shapes[i];i++)body.addShape(deserializeShape(shapeMsg),deserializeVec3(message.shapeOffsets[i]),deserializeQuaternion(message.shapeOrientations[i]));return body[ID]=message.id,body},module.exports.serializeShape=serializeShape,module.exports.deserializeShape=deserializeShape,module.exports.serializeConstraint=function(constraint){var message={id:constraint[ID],type:constraint.type,maxForce:constraint.maxForce,bodyA:constraint.bodyA[ID],bodyB:constraint.bodyB[ID]};switch(constraint.type){case"LockConstraint":break;case"DistanceConstraint":message.distance=constraint.distance;break;case"HingeConstraint":case"ConeTwistConstraint":message.axisA=serializeVec3(constraint.axisA),message.axisB=serializeVec3(constraint.axisB),message.pivotA=serializeVec3(constraint.pivotA),message.pivotB=serializeVec3(constraint.pivotB);break;case"PointToPointConstraint":message.pivotA=serializeVec3(constraint.pivotA),message.pivotB=serializeVec3(constraint.pivotB);break;default:throw new Error("Unexpected constraint type: "+constraint.type+'. You may need to manually set `constraint.type = "FooConstraint";`.')}return message},module.exports.deserializeConstraint=function(message,bodies){var constraint,TypedConstraint=CANNON[message.type],bodyA=bodies[message.bodyA],bodyB=bodies[message.bodyB];switch(message.type){case"LockConstraint":constraint=new CANNON.LockConstraint(bodyA,bodyB,message);break;case"DistanceConstraint":constraint=new CANNON.DistanceConstraint(bodyA,bodyB,message.distance,message.maxForce);break;case"HingeConstraint":case"ConeTwistConstraint":constraint=new TypedConstraint(bodyA,bodyB,{pivotA:deserializeVec3(message.pivotA),pivotB:deserializeVec3(message.pivotB),axisA:deserializeVec3(message.axisA),axisB:deserializeVec3(message.axisB),maxForce:message.maxForce});break;case"PointToPointConstraint":constraint=new CANNON.PointToPointConstraint(bodyA,deserializeVec3(message.pivotA),bodyB,deserializeVec3(message.pivotB),message.maxForce);break;default:throw new Error("Unexpected constraint type: "+message.type)}return constraint[ID]=message.id,constraint},module.exports.serializeContact=function(contact){return{bi:contact.bi[ID],bj:contact.bj[ID],ni:serializeVec3(contact.ni),ri:serializeVec3(contact.ri),rj:serializeVec3(contact.rj)}},module.exports.deserializeContact=function(message,bodies){return{bi:bodies[message.bi],bj:bodies[message.bj],ni:deserializeVec3(message.ni),ri:deserializeVec3(message.ri),rj:deserializeVec3(message.rj)}},module.exports.serializeVec3=serializeVec3,module.exports.deserializeVec3=deserializeVec3,module.exports.serializeQuaternion=serializeQuaternion,module.exports.deserializeQuaternion=deserializeQuaternion},{"./math":30,"cannon-es":5}]},{},[1]); \ No newline at end of file