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 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() { 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); } }