From 15301353e72d730178ed709770d78ebca2d98191 Mon Sep 17 00:00:00 2001 From: Vincent Fretin Date: Sun, 4 Feb 2024 15:28:36 +0100 Subject: [PATCH] build dist --- dist/networked-aframe.js | 4 ++-- dist/networked-aframe.min.js | 2 +- package-lock.json | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dist/networked-aframe.js b/dist/networked-aframe.js index d7b51ce0..c143316a 100644 --- a/dist/networked-aframe.js +++ b/dist/networked-aframe.js @@ -47,7 +47,7 @@ eval("// Patched version of fast-deep-equal which does not\n// allocate memory v \*************************/ /***/ ((module, __unused_webpack_exports, __webpack_require__) => { -eval("var options = __webpack_require__(/*! ./options */ \"./src/options.js\");\nvar utils = __webpack_require__(/*! ./utils */ \"./src/utils.js\");\nvar NafLogger = __webpack_require__(/*! ./NafLogger */ \"./src/NafLogger.js\");\nvar Schemas = __webpack_require__(/*! ./Schemas */ \"./src/Schemas.js\");\nvar NetworkEntities = __webpack_require__(/*! ./NetworkEntities */ \"./src/NetworkEntities.js\");\nvar NetworkConnection = __webpack_require__(/*! ./NetworkConnection */ \"./src/NetworkConnection.js\");\nvar AdapterFactory = __webpack_require__(/*! ./adapters/AdapterFactory */ \"./src/adapters/AdapterFactory.js\");\nvar naf = {};\nnaf.app = '';\nnaf.room = '';\nnaf.clientId = '';\nnaf.options = options;\nnaf.utils = utils;\nnaf.log = new NafLogger();\nnaf.schemas = new Schemas();\nnaf.version = \"0.12.0\";\nnaf.adapters = new AdapterFactory();\nvar entities = new NetworkEntities();\nvar connection = new NetworkConnection(entities);\nnaf.connection = connection;\nnaf.entities = entities;\nmodule.exports = window.NAF = naf;\n\n//# sourceURL=webpack://networked-aframe/./src/NafIndex.js?"); +eval("var options = __webpack_require__(/*! ./options */ \"./src/options.js\");\nvar utils = __webpack_require__(/*! ./utils */ \"./src/utils.js\");\nvar NafLogger = __webpack_require__(/*! ./NafLogger */ \"./src/NafLogger.js\");\nvar Schemas = __webpack_require__(/*! ./Schemas */ \"./src/Schemas.js\");\nvar NetworkEntities = __webpack_require__(/*! ./NetworkEntities */ \"./src/NetworkEntities.js\");\nvar NetworkConnection = __webpack_require__(/*! ./NetworkConnection */ \"./src/NetworkConnection.js\");\nvar AdapterFactory = __webpack_require__(/*! ./adapters/AdapterFactory */ \"./src/adapters/AdapterFactory.js\");\nvar naf = {};\nnaf.app = '';\nnaf.room = '';\nnaf.clientId = '';\nnaf.options = options;\nnaf.utils = utils;\nnaf.log = new NafLogger();\nnaf.schemas = new Schemas();\nnaf.version = \"0.12.1\";\nnaf.adapters = new AdapterFactory();\nvar entities = new NetworkEntities();\nvar connection = new NetworkConnection(entities);\nnaf.connection = connection;\nnaf.entities = entities;\nmodule.exports = window.NAF = naf;\n\n//# sourceURL=webpack://networked-aframe/./src/NafIndex.js?"); /***/ }), @@ -177,7 +177,7 @@ eval("/* global AFRAME, NAF, THREE */\nvar naf = __webpack_require__(/*! ../NafI \***************************************************/ /***/ (() => { -eval("function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }\nfunction _nonIterableSpread() { throw new TypeError(\"Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\"); }\nfunction _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === \"string\") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === \"Object\" && o.constructor) n = o.constructor.name; if (n === \"Map\" || n === \"Set\") return Array.from(o); if (n === \"Arguments\" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }\nfunction _iterableToArray(iter) { if (typeof Symbol !== \"undefined\" && iter[Symbol.iterator] != null || iter[\"@@iterator\"] != null) return Array.from(iter); }\nfunction _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }\nfunction _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }\n/* global THREE, AFRAME, NAF */\n\n/**\n * Represent any 6dof controller as a hand with live tracked animated gestures and normalized events.\n *\n * Updated and modified adaption of https://github.com/aframevr/aframe/blob/master/src/components/hand-controls.js\n * Designed for use with NAF, but can also be used without NAF.\n *\n * Auto-detects appropriate controller.\n * Handles common events coming from the detected vendor-specific controls.\n * Loads hand model with gestures that are applied based on the button pressed.\n * Translate button events to semantic hand-related event names:\n * (fist, thumbUp, point, hold, open, pistol) + (start, end)\n * note that the events were incomplete/broken in prior implementation, and therefore a breaking change\n */\n\n// this is a way to bypass adding the template in to the HTML, as well as the already awkward NAF.schemas.add \n// workaround. this makes the use of this component for the end user much simpler.\nfunction addHandTemplate(hand) {\n var templateOuter = document.createElement('template');\n var templateInner = document.createElement('a-entity');\n templateOuter.id = \"\".concat(hand, \"-hand-default-template\");\n templateInner.setAttribute('rotation', '0 0 0'); // to set the YXZ order\n templateInner.setAttribute('networked-hand-controls', \"hand: \".concat(hand));\n templateOuter.appendChild(templateInner);\n var refTemplateId = \"#\".concat(templateOuter.id);\n NAF.schemas.schemaDict[refTemplateId] = {\n template: refTemplateId,\n components: [{\n component: 'position',\n requiresNetworkUpdate: NAF.utils.vectorRequiresUpdate(0.001)\n }, {\n component: 'rotation',\n requiresNetworkUpdate: NAF.utils.vectorRequiresUpdate(0.5)\n }, 'networked-hand-controls']\n };\n NAF.schemas.templateCache[refTemplateId] = templateOuter;\n}\n[\"left\", \"right\"].forEach(addHandTemplate);\nAFRAME.registerComponent('networked-hand-controls', {\n schema: {\n color: {\n \"default\": 'white',\n type: 'color'\n },\n hand: {\n type: \"string\",\n \"default\": 'left',\n oneOf: ['right', 'left']\n },\n handModelStyle: {\n type: \"string\",\n \"default\": 'highPoly',\n oneOf: ['lowPoly', 'highPoly', 'toon', 'controller']\n },\n // these are set internally if we use type 'controller' for handModelStyle\n controllerComponent: {\n type: \"string\",\n \"default\": ''\n },\n webxrControllerProfiles: {\n type: \"string\",\n \"default\": ''\n },\n controllerEvent: {\n \"default\": JSON.stringify({}),\n type: \"string\"\n },\n controllerModelUpdate: {\n \"default\": [],\n type: \"array\"\n },\n // these are for specifying a custom hand model URL; only allowed at init, not via update\n // (must correspond to existing models and have matching animations to work properly)\n customHandModelURL: {\n type: \"string\",\n \"default\": ''\n },\n // this is set internally when using a hand model\n gesture: {\n type: \"string\",\n \"default\": 'open'\n },\n // this is set internally, hands are invisible until webxr controller is connected\n visible: {\n type: \"bool\",\n \"default\": false\n }\n },\n init: function init() {\n var _this = this;\n this.setup();\n this.rendererSystem = this.el.sceneEl.systems.renderer;\n if (this.data.handModelStyle !== \"controller\") {\n this.addHandModel();\n }\n NAF.utils.getNetworkedEntity(this.el).then(function (networkedEl) {\n // Here networkedEl may be different than this.el if we don't use nested\n // networked components for hands.\n _this.local = networkedEl.components.networked.createdByMe();\n })[\"catch\"](function () {\n _this.local = true;\n }).then(function () {\n if (_this.local) {\n _this.addControllerComponents(_this.data.handModelStyle === \"controller\");\n // Bind all functions, the listeners are only registered later for local avatar.\n for (var evtName in _this.buttonEventMap) {\n var _this$handleButton;\n _this.eventFunctionMap[_this.data.hand][evtName] = (_this$handleButton = _this.handleButton).bind.apply(_this$handleButton, [_this].concat(_toConsumableArray(_this.buttonEventMap[evtName])));\n }\n for (var _evtName in _this.visibleListeners) {\n _this.eventFunctionMap[_this.data.hand][_evtName] = _this.visibleListeners[_evtName].bind(_this);\n }\n } else {\n // Adding a class to easily see what the entity is in the aframe\n // inspector. This is shown in the class field in the panel after you\n // click the entity.\n _this.el.classList.add('naf-remote-hand');\n }\n });\n },\n play: function play() {\n if (this.local) {\n this.addEventListeners();\n }\n },\n pause: function pause() {\n if (this.local) {\n this.removeEventListeners();\n }\n },\n tick: function tick(time, delta) {\n var _this$getMesh;\n if ((_this$getMesh = this.getMesh()) !== null && _this$getMesh !== void 0 && _this$getMesh.mixer) this.getMesh().mixer.update(delta / 1000);\n },\n update: function update(oldData) {\n var _this2 = this;\n if (oldData.visible != this.data.visible) {\n this.el.object3D.visible = this.data.visible;\n }\n\n // this block is for handling updates() from hand model to controller model, or vice versa.\n if (oldData.handModelStyle && oldData.handModelStyle !== this.data.handModelStyle) {\n // first, remove old model\n if (this.getMesh()) this.el.removeObject3D(this.str.mesh);\n ['gltf-model', 'obj-model'].forEach(function (modelComponent) {\n if (_this2.el.components[modelComponent]) {\n _this2.el.removeAttribute(modelComponent);\n }\n });\n // then, add correct model\n if (this.data.handModelStyle !== \"controller\") {\n this.addHandModel();\n } else {\n // it would be nice if this worked, but those components don't handle this option in their update() methods:\n // this.el.setAttribute(this.data.controllerComponent, 'model', true)\n\n // so we first remove the controller component\n if (this.data.controllerComponent && this.el.components[this.data.controllerComponent]) {\n this.el.removeAttribute(this.data.controllerComponent);\n }\n if (!this.local) {\n if (!this.injectedController && this.data.controllerComponent && this.data.webxrControllerProfiles[0]) {\n this.injectRemoteControllerModel();\n }\n } else {\n this.addControllerComponents(true);\n // controllerconnected event won't fire again, so we have to call this manually:\n this.el.components[this.data.controllerComponent].injectTrackedControls({\n profiles: JSON.parse(this.data.webxrControllerProfiles)\n });\n }\n }\n }\n if (oldData.color && this.data.handModelStyle !== this.str.controller && oldData.color !== this.data.color) {\n this.updateHandMeshColor();\n }\n\n // this block is for receiving+handling networked hand gestures and button events for controller model updates\n // and for initial injection of remote controller model\n if (!this.local) {\n if (this.data.handModelStyle !== this.str.controller && this.data.gesture !== oldData.gesture) {\n this.handleGesture(this.data.gesture, oldData.gesture);\n } else if (this.data.handModelStyle === this.str.controller) {\n if (!this.injectedController && this.data.controllerComponent && this.data.webxrControllerProfiles[0]) {\n this.injectRemoteControllerModel();\n }\n if (oldData.controllerEvent && oldData.controllerEvent !== this.data.controllerEvent) {\n var controllerEvent = JSON.parse(this.data.controllerEvent);\n this.el.components[this.data.controllerComponent][controllerEvent.type === 'thumbstickmoved' ? 'onThumbstickMoved' : 'onButtonChanged'](controllerEvent);\n }\n if (oldData.controllerModelUpdate && (oldData.controllerModelUpdate[0] !== this.data.controllerModelUpdate[0] || oldData.controllerModelUpdate[1] !== this.data.controllerModelUpdate[1])) {\n var _this$el$components$t;\n (_this$el$components$t = this.el.components[this.data.controllerComponent]).updateModel.apply(_this$el$components$t, _toConsumableArray(this.data.controllerModelUpdate));\n }\n }\n }\n },\n remove: function remove() {\n this.el.removeObject3D(this.str.mesh);\n },\n addHandModel: function addHandModel() {\n var _this3 = this;\n this.loader = new THREE.GLTFLoader();\n this.loader.setCrossOrigin('anonymous');\n var handmodelUrl = this.MODEL_BASE + this.MODEL_NAMES[this.data.handModelStyle + this.data.hand.charAt(0).toUpperCase() + this.data.hand.slice(1)];\n this.loader.load(this.data.customHandModelURL || handmodelUrl, function (gltf) {\n var newMesh = gltf.scene.children[0];\n var handModelOrientation = _this3.data.hand === 'left' ? Math.PI / 2 : -Math.PI / 2;\n newMesh.mixer = new THREE.AnimationMixer(newMesh);\n _this3.clips = gltf.animations;\n _this3.clips.forEach(function (clip) {\n _this3.clipNameToClip[clip.name] = clip;\n });\n _this3.el.setObject3D(_this3.str.mesh, newMesh);\n var handMaterial = newMesh.children[1].material;\n handMaterial.color = new THREE.Color(_this3.data.color);\n _this3.rendererSystem.applyColorCorrection(handMaterial.color);\n newMesh.position.set(0, 0, 0);\n newMesh.rotation.set(0, 0, handModelOrientation);\n });\n },\n updateHandMeshColor: function updateHandMeshColor() {\n var mesh = this.getMesh();\n if (!mesh) return;\n var handMaterial = mesh.children[1].material;\n handMaterial.color.set(this.data.color);\n this.rendererSystem.applyColorCorrection(handMaterial.color);\n },\n controllerComponents: ['magicleap-controls', 'vive-controls', 'oculus-touch-controls', 'windows-motion-controls', 'hp-mixed-reality-controls',\n // these were missing from the original hand-controls component:\n 'valve-index-controls',\n // some older models that it doesn't hurt to include:\n 'oculus-go-controls', 'gearvr-controls', 'daydream-controls', 'vive-focus-controls'],\n addControllerComponents: function addControllerComponents(useControllerModel) {\n var _this4 = this;\n // this adds all controller components, with wrappers set up to capture and broadcast model found and relevant button presses\n this.controllerComponents.forEach(function (controllerComponentName) {\n if (_this4.data.controllerComponent && _this4.data.controllerComponent !== controllerComponentName) {\n return;\n }\n _this4.el.setAttribute(controllerComponentName, {\n hand: _this4.data.hand,\n model: useControllerModel\n });\n\n // (note: this could possibly be rewritten to rely on the controllerconnected event)\n _this4.el.components[controllerComponentName].injectTrackedControls = _this4.injectControlsWrapper.bind(_this4, _this4.el.components[controllerComponentName].injectTrackedControls.bind(_this4.el.components[controllerComponentName]), controllerComponentName);\n });\n this.isViveController();\n },\n injectControlsWrapper: function injectControlsWrapper(originalFn, controllerComponentName, webxrControllerSymbol) {\n // captures and rebroadcasts the model found by AFRAME.utils.trackedControls.findMatchingControllerWebXR\n this.el.setAttribute('networked-hand-controls', {\n controllerComponent: controllerComponentName,\n webxrControllerProfiles: JSON.stringify(webxrControllerSymbol.profiles)\n });\n originalFn(webxrControllerSymbol);\n\n // this function handles mesh color updates\n this.el.components[controllerComponentName].updateModel = this.updateModelWrapper.bind(this, this.el.components[controllerComponentName].updateModel.bind(this.el.components[controllerComponentName]));\n this.el.components[controllerComponentName].onButtonChanged = this.onButtonChangedWrapper.bind(this, this.el.components[controllerComponentName].onButtonChanged);\n this.el.components[controllerComponentName].onThumbstickMoved = this.onThumbstickMovedWrapper.bind(this, this.el.components[controllerComponentName].onThumbstickMoved);\n\n // we want to strip the listeners pointing to the original functions, and then re-add them with our wrapped functions\n this.el.components[controllerComponentName].removeEventListeners();\n this.el.components[controllerComponentName].addEventListeners();\n },\n onButtonChangedWrapper: function onButtonChangedWrapper(originalFn, evt) {\n this.el.setAttribute('networked-hand-controls', this.str.controllerEvent, JSON.stringify({\n detail: evt.detail,\n type: evt.type\n }));\n originalFn(evt);\n },\n onThumbstickMovedWrapper: function onThumbstickMovedWrapper(originalFn, evt) {\n this.el.setAttribute('networked-hand-controls', this.str.controllerEvent, JSON.stringify({\n detail: evt.detail,\n type: evt.type\n }));\n originalFn(evt);\n },\n updateModelWrapper: function updateModelWrapper(originalFn, buttonName, evtName) {\n // capture and rebroadcast controller events (only used if using controller model instead of hand)\n if (!this.btnEvtMap[buttonName]) {\n this.btnEvtMap[buttonName] = {};\n }\n if (!this.btnEvtMap[buttonName][evtName]) {\n this.btnEvtMap[buttonName][evtName] = \"\".concat(buttonName, \",\").concat(evtName);\n }\n this.el.setAttribute('networked-hand-controls', \"controllerModelUpdate\", this.btnEvtMap[buttonName][evtName]);\n originalFn(buttonName, evtName);\n },\n catchPoseTrackingInitialization: function catchPoseTrackingInitialization() {\n this.catchAndRemoveWebXRTracking = function catchAndRemoveWebXRTracking(evt) {\n if (evt.detail.name !== 'tracked-controls-webxr') {\n return;\n }\n this.el.removeEventListener('componentinitialized', this.catchAndRemoveWebXRTracking);\n this.el.removeAttribute('tracked-controls-webxr');\n }.bind(this);\n this.el.addEventListener('componentinitialized', this.catchAndRemoveWebXRTracking);\n },\n injectRemoteControllerModel: function injectRemoteControllerModel() {\n // this is for a networked controller entity, where we want to show a non-hand controller model\n // we add the relevant controller component, and then hack it just a bit to:\n // A) convince it the model show without relation to whether one is actually locally connected, and \n // B) to not track _local_ pose data (so, no prevent/remove tracked-controller component)\n\n // we pause the element so we can prevent pose tracking from being added.\n this.el.pause();\n\n // we add the actual component, to get the model generated and button meshes and event handling\n this.el.setAttribute(this.data.controllerComponent, {\n hand: this.data.hand,\n model: true\n // could also optionally support custom buttonColor, buttonTouchColor, buttonHighlightColor \n });\n\n // in new design, we leave this, as the controller needs to emit some of these events to itself\n // in order to get mesh color updates\n // this.el.components[this.data.controllerComponent].removeEventListeners();\n\n // we don't want the remote model to listen to local button/trigger/thumbstick events, though \n this.el.components[this.data.controllerComponent].removeControllersUpdateListener();\n\n // some older controller components aren't as well written, and so we have to allow pose tracking to be added and then remove it\n if (!this.el.components[this.data.controllerComponent].checkIfControllerPresent || !this.el.components[this.data.controllerComponent].loadModel) {\n console.warn(\"fallback path: will add and then disable pose tracking\", this.data.controllerComponent, this.el, this.el.components);\n\n // however, we do still want _one_ event listener, so we add the button meshes\n this.el.addEventListener('model-loaded', this.el.components[this.data.controllerComponent].onModelLoaded);\n this.catchPoseTrackingInitialization();\n // here we have no loadModel() to use directly, so we fully inject (bypassing controller detection)\n // and catch and remove pose tracking once it is added.\n this.el.components[this.data.controllerComponent].injectTrackedControls({\n profiles: JSON.parse(this.data.webxrControllerProfiles)\n });\n } else {\n // ideal, default path, for controller components (like oculus-touch-controls), that have separate loadModel() function\n\n // this prevents injectTrackedControllers from running, which prevents the \n // tracked-controls-webxr component from being added, which is responsible for pose tracking\n // (which we are disabling, since we want this controller to follow this NAF entity instead)\n this.el.components[this.data.controllerComponent].checkIfControllerPresent = function (x) {\n return x;\n };\n this.el.components[this.data.controllerComponent].injectTrackedControls = function (x) {\n return x;\n };\n\n // we need to fire this listener to load the button meshes\n this.el.addEventListener('model-loaded', this.el.components[this.data.controllerComponent].onModelLoaded);\n // we load the model indicated by the remote user's headset\n this.el.components[this.data.controllerComponent].loadModel({\n profiles: JSON.parse(this.data.webxrControllerProfiles)\n });\n }\n this.el.play();\n this.injectedController = true;\n },\n getMesh: function getMesh() {\n return this.el.getObject3D(this.str.mesh);\n },\n // local hands are controlled by one's controllers, non-local hands are controlled by updates via NAF\n local: false,\n eventFunctionMap: {\n left: {},\n right: {}\n },\n buttonEventMap: {\n gripdown: ['grip', 'down'],\n gripup: ['grip', 'up'],\n trackpaddown: ['trackpad', 'down'],\n trackpadup: ['trackpad', 'up'],\n trackpadtouchstart: ['trackpad', 'touchstart'],\n trackpadtouchend: ['trackpad', 'touchend'],\n triggerdown: ['trigger', 'down'],\n triggerup: ['trigger', 'up'],\n triggertouchstart: ['trigger', 'touchstart'],\n triggertouchend: ['trigger', 'touchend'],\n griptouchstart: ['grip', 'touchstart'],\n griptouchend: ['grip', 'touchend'],\n abuttontouchstart: ['AorX', 'touchstart'],\n abuttontouchend: ['AorX', 'touchend'],\n bbuttontouchstart: ['BorY', 'touchstart'],\n bbuttontouchend: ['BorY', 'touchend'],\n xbuttontouchstart: ['AorX', 'touchstart'],\n xbuttontouchend: ['AorX', 'touchend'],\n ybuttontouchstart: ['BorY', 'touchstart'],\n ybuttontouchend: ['BorY', 'touchend'],\n surfacetouchstart: ['surface', 'touchstart'],\n surfacetouchend: ['surface', 'touchend']\n\n // these are not used for any existing gestures\n // thumbstickdown: ['thumbstick', 'down'],\n // thumbstickup: ['thumbstick', 'up'], \n },\n visibleListeners: {\n // (note: while `this.el` looks wrong here, we use .bind() on it within init())\n controllerconnected: function controllerconnected() {\n this.el.setAttribute('networked-hand-controls', 'visible', true);\n },\n controllerdisconnected: function controllerdisconnected() {\n this.el.setAttribute('networked-hand-controls', 'visible', false);\n }\n },\n addEventListeners: function addEventListeners() {\n for (var evtName in this.buttonEventMap) {\n this.el.addEventListener(evtName, this.eventFunctionMap[this.data.hand][evtName]);\n }\n for (var _evtName2 in this.visibleListeners) {\n this.el.addEventListener(_evtName2, this.eventFunctionMap[this.data.hand][_evtName2]);\n }\n },\n removeEventListeners: function removeEventListeners() {\n for (var evtName in this.buttonEventMap) {\n this.el.removeEventListener(evtName, this.eventFunctionMap[this.data.hand][evtName]);\n }\n for (var _evtName3 in this.visibleListeners) {\n this.el.removeEventListener(_evtName3, this.eventFunctionMap[this.data.hand][_evtName3]);\n }\n },\n // to minimize garbage collection, prevents generating huge numbers of one-time-use strings\n str: {\n nafHandControls: \"networked-hand-controls\",\n mesh: \"mesh\",\n gesture: \"gesture\",\n down: 'down',\n touchstart: 'touchstart',\n fist: \"fist\",\n point: \"point\",\n thumbUp: \"thumbUp\",\n pistol: \"pistol\",\n hold: \"hold\",\n open: \"open\",\n controller: \"controller\",\n controllerEvent: \"controllerEvent\"\n },\n handleButton: function handleButton(button, evt) {\n // this function is only for generating networked gestures with hand model\n if (this.data.handModelStyle === 'controller') {\n return;\n }\n this.isContact = evt === this.str.down || evt === this.str.touchstart;\n if (this.isContact === this.contact[button]) {\n return;\n }\n this.contact[button] = this.isContact;\n this.lastGesture = this.data.gesture;\n this.determineGesture();\n if (this.gesture === this.lastGesture) {\n return;\n }\n\n // set new gesture into schema to propagate via NAF\n this.el.setAttribute(this.str.nafHandControls, this.str.gesture, this.gesture);\n this.handleGesture();\n },\n handleGesture: function handleGesture() {\n if (this.data.gesture === this.lastGesture) {\n return;\n }\n this.playAnimation();\n this.emitGestureEvents();\n },\n determineGesture: function determineGesture() {\n this.gripIsActive = this.contact.grip;\n this.triggerIsActive = this.contact.trigger;\n this.thumbIsActive = this.contact.trackpad || this.contact.surface;\n this.contact.AorX || this.contact.BorY;\n if (!this.isVive) {\n this.gesture = this.gripIsActive && this.thumbIsActive && this.triggerIsActive ? this.str.fist : this.gripIsActive && this.thumbIsActive && !this.triggerIsActive ? this.str.point : this.gripIsActive && !this.thumbIsActive && this.triggerIsActive ? this.str.thumbUp : this.gripIsActive && !this.thumbIsActive && !this.triggerIsActive ? this.str.pistol : this.triggerIsActive ? this.str.hold : this.str.open;\n } else {\n // Vive handling was supposedly left incomplete\n this.gesture = this.gripIsActive || this.triggerIsActive ? this.str.fist : this.contact.trackpad ? this.str.point : this.str.open;\n }\n },\n emitGestureEvents: function emitGestureEvents() {\n var _this$eventNames$this, _this$eventNames$this2;\n this.el.emit((_this$eventNames$this = this.eventNames[this.lastGesture]) === null || _this$eventNames$this === void 0 ? void 0 : _this$eventNames$this.inactive);\n this.el.emit((_this$eventNames$this2 = this.eventNames[this.data.gesture]) === null || _this$eventNames$this2 === void 0 ? void 0 : _this$eventNames$this2.active);\n },\n // mapping rather than function to reduce garbage collection\n eventNames: {\n fist: {\n active: \"fiststart\",\n inactive: \"fistend\"\n },\n thumbUp: {\n active: \"thumbupstart\",\n inactive: \"thumbupend\"\n },\n point: {\n active: \"pointstart\",\n inactive: \"pointend\"\n },\n hold: {\n active: \"holdstart\",\n inactive: \"holdend\"\n },\n open: {\n active: \"openstart\",\n inactive: \"openend\"\n },\n pistol: {\n active: \"pistolstart\",\n inactive: \"pistolend\"\n }\n },\n playAnimation: function playAnimation() {\n // this function seems like it could be improved\n // implementation is from the original hand-controls component\n\n if (!this.getMesh()) {\n return;\n }\n\n // Stop all current animations\n this.getMesh().mixer.stopAllAction();\n\n // Grab clip action\n this.clip = this.clipNameToClip[this.ANIMATIONS[this.data.gesture]];\n this.toAction = this.getMesh().mixer.clipAction(this.clip);\n this.toAction.clampWhenFinished = true;\n this.toAction.loop = THREE.LoopRepeat;\n this.toAction.repetitions = 0;\n this.toAction.timeScale = this.reverse ? -1 : 1;\n this.toAction.time = this.reverse ? this.clip.duration : 0;\n this.toAction.weight = 1;\n if (!this.lastGesture || this.data.gesture === this.lastGesture) {\n this.getMesh().mixer.stopAllAction();\n this.toAction.play();\n return;\n }\n\n // Animate or crossfade from gesture to gesture\n this.clip = this.clipNameToClip[this.ANIMATIONS[this.lastGesture]];\n this.fromAction = this.getMesh().mixer.clipAction(this.clip);\n this.fromAction.weight = 0.15;\n this.fromAction.play();\n this.toAction.play();\n this.fromAction.crossFadeTo(this.toAction, 0.15, true);\n },\n // see https://github.com/aframevr/assets\n MODEL_BASE: 'https://cdn.aframe.io/controllers/hands/',\n MODEL_NAMES: {\n toonLeft: 'leftHand.glb',\n toonRight: 'rightHand.glb',\n lowPolyLeft: 'leftHandLow.glb',\n lowPolyRight: 'rightHandLow.glb',\n highPolyLeft: 'leftHandHigh.glb',\n highPolyRight: 'rightHandHigh.glb'\n },\n clipNameToClip: {},\n // map from internal state names to the names of the clips as specified within the models\n ANIMATIONS: {\n open: 'Open',\n point: 'Point',\n pistol: 'Point + Thumb',\n fist: 'Fist',\n hold: 'Hold',\n thumbUp: 'Thumb Up'\n },\n isVive: false,\n isViveController: function isViveController() {\n var controller = this.el.components['tracked-controls'] && this.el.components['tracked-controls'].controller;\n this.isVive = controller && (controller.id && controller.id.indexOf('OpenVR ') === 0 || controller.profiles && controller.profiles[0] && controller.profiles[0] === 'htc-vive');\n },\n btnEvtMap: {},\n // dynamically populated, but actually helpful if prototype is modified\n setup: function setup() {\n // preventing garbage collection by making sure these don't end up as part of the prototype\n this.contact = {\n grip: false,\n trigger: false,\n surface: false,\n trackpad: false,\n AorX: false,\n BorY: false\n };\n this.reverse = false;\n this.isContact = false;\n this.injectedController = false;\n this.lastGesture = \"\";\n this.gesture = \"\";\n this.gripIsActive = false;\n this.triggerIsActive = false;\n this.thumbIsActive = false;\n this.clip = null;\n this.toAction = null;\n this.fromAction = null;\n }\n});\n\n//# sourceURL=webpack://networked-aframe/./src/components/networked-hand-controls.js?"); +eval("function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }\nfunction _nonIterableSpread() { throw new TypeError(\"Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\"); }\nfunction _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === \"string\") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === \"Object\" && o.constructor) n = o.constructor.name; if (n === \"Map\" || n === \"Set\") return Array.from(o); if (n === \"Arguments\" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }\nfunction _iterableToArray(iter) { if (typeof Symbol !== \"undefined\" && iter[Symbol.iterator] != null || iter[\"@@iterator\"] != null) return Array.from(iter); }\nfunction _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }\nfunction _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }\n/* global THREE, AFRAME, NAF */\n\n/**\n * Represent any 6dof controller as a hand with live tracked animated gestures and normalized events.\n *\n * Updated and modified adaption of https://github.com/aframevr/aframe/blob/master/src/components/hand-controls.js\n * Designed for use with NAF, but can also be used without NAF.\n *\n * Auto-detects appropriate controller.\n * Handles common events coming from the detected vendor-specific controls.\n * Loads hand model with gestures that are applied based on the button pressed.\n * Translate button events to semantic hand-related event names:\n * (fist, thumbUp, point, hold, open, pistol) + (start, end)\n * note that the events were incomplete/broken in prior implementation, and therefore a breaking change\n */\n\n// this is a way to bypass adding the template in to the HTML, as well as the already awkward NAF.schemas.add \n// workaround. this makes the use of this component for the end user much simpler.\nfunction addHandTemplate(hand) {\n var templateOuter = document.createElement('template');\n var templateInner = document.createElement('a-entity');\n templateOuter.id = \"\".concat(hand, \"-hand-default-template\");\n templateInner.setAttribute('rotation', '0 0 0'); // to set the YXZ order\n templateInner.setAttribute('networked-hand-controls', \"hand: \".concat(hand));\n templateOuter.appendChild(templateInner);\n var refTemplateId = \"#\".concat(templateOuter.id);\n NAF.schemas.schemaDict[refTemplateId] = {\n template: refTemplateId,\n components: [{\n component: 'position',\n requiresNetworkUpdate: NAF.utils.vectorRequiresUpdate(0.001)\n }, {\n component: 'rotation',\n requiresNetworkUpdate: NAF.utils.vectorRequiresUpdate(0.5)\n }, 'networked-hand-controls']\n };\n NAF.schemas.templateCache[refTemplateId] = templateOuter;\n}\n[\"left\", \"right\"].forEach(addHandTemplate);\nAFRAME.registerComponent('networked-hand-controls', {\n schema: {\n color: {\n \"default\": 'white',\n type: 'color'\n },\n hand: {\n type: \"string\",\n \"default\": 'left',\n oneOf: ['right', 'left']\n },\n handModelStyle: {\n type: \"string\",\n \"default\": 'highPoly',\n oneOf: ['lowPoly', 'highPoly', 'toon', 'controller']\n },\n // these are set internally if we use type 'controller' for handModelStyle\n controllerComponent: {\n type: \"string\",\n \"default\": ''\n },\n webxrControllerProfiles: {\n type: \"string\",\n \"default\": ''\n },\n controllerEvent: {\n \"default\": JSON.stringify({}),\n type: \"string\"\n },\n controllerModelUpdate: {\n \"default\": [],\n type: \"array\"\n },\n // these are for specifying a custom hand model URL; only allowed at init, not via update\n // (must correspond to existing models and have matching animations to work properly)\n customHandModelURL: {\n type: \"string\",\n \"default\": ''\n },\n // this is set internally when using a hand model\n gesture: {\n type: \"string\",\n \"default\": 'open'\n },\n // this is set internally, hands are invisible until webxr controller is connected\n visible: {\n type: \"bool\",\n \"default\": false\n }\n },\n init: function init() {\n var _this = this;\n this.setup();\n this.rendererSystem = this.el.sceneEl.systems.renderer;\n if (this.data.handModelStyle !== \"controller\") {\n this.addHandModel();\n }\n NAF.utils.getNetworkedEntity(this.el).then(function (networkedEl) {\n // Here networkedEl may be different than this.el if we don't use nested\n // networked components for hands.\n _this.local = networkedEl.components.networked.createdByMe();\n })[\"catch\"](function () {\n _this.local = true;\n }).then(function () {\n if (_this.local) {\n _this.addControllerComponents(_this.data.handModelStyle === \"controller\");\n // Bind all functions, the listeners are only registered later for local avatar.\n for (var evtName in _this.buttonEventMap) {\n var _this$handleButton;\n _this.eventFunctionMap[_this.data.hand][evtName] = (_this$handleButton = _this.handleButton).bind.apply(_this$handleButton, [_this].concat(_toConsumableArray(_this.buttonEventMap[evtName])));\n }\n for (var _evtName in _this.visibleListeners) {\n _this.eventFunctionMap[_this.data.hand][_evtName] = _this.visibleListeners[_evtName].bind(_this);\n }\n } else {\n // Adding a class to easily see what the entity is in the aframe\n // inspector. This is shown in the class field in the panel after you\n // click the entity.\n _this.el.classList.add('naf-remote-hand');\n }\n });\n },\n play: function play() {\n if (this.local) {\n this.addEventListeners();\n }\n },\n pause: function pause() {\n if (this.local) {\n this.removeEventListeners();\n }\n },\n tick: function tick(time, delta) {\n var _this$getMesh;\n if ((_this$getMesh = this.getMesh()) !== null && _this$getMesh !== void 0 && _this$getMesh.mixer) this.getMesh().mixer.update(delta / 1000);\n },\n update: function update(oldData) {\n var _this2 = this;\n if (oldData.visible != this.data.visible) {\n this.el.object3D.visible = this.data.visible;\n }\n\n // this block is for handling updates() from hand model to controller model, or vice versa.\n if (oldData.handModelStyle && oldData.handModelStyle !== this.data.handModelStyle) {\n // first, remove old model\n if (this.getMesh()) this.el.removeObject3D(this.str.mesh);\n ['gltf-model', 'obj-model'].forEach(function (modelComponent) {\n if (_this2.el.components[modelComponent]) {\n _this2.el.removeAttribute(modelComponent);\n }\n });\n // then, add correct model\n if (this.data.handModelStyle !== \"controller\") {\n this.addHandModel();\n } else {\n // it would be nice if this worked, but those components don't handle this option in their update() methods:\n // this.el.setAttribute(this.data.controllerComponent, 'model', true)\n\n // so we first remove the controller component\n if (this.data.controllerComponent && this.el.components[this.data.controllerComponent]) {\n this.el.removeAttribute(this.data.controllerComponent);\n }\n if (!this.local) {\n if (!this.injectedController && this.data.controllerComponent && this.data.webxrControllerProfiles[0]) {\n this.injectRemoteControllerModel();\n }\n } else {\n this.addControllerComponents(true);\n // controllerconnected event won't fire again, so we have to call this manually:\n this.el.components[this.data.controllerComponent].injectTrackedControls({\n profiles: JSON.parse(this.data.webxrControllerProfiles)\n });\n }\n }\n }\n if (oldData.color && this.data.handModelStyle !== this.str.controller && oldData.color !== this.data.color) {\n this.updateHandMeshColor();\n }\n\n // this block is for receiving+handling networked hand gestures and button events for controller model updates\n // and for initial injection of remote controller model\n if (!this.local) {\n if (this.data.handModelStyle !== this.str.controller && this.data.gesture !== oldData.gesture) {\n this.handleGesture(this.data.gesture, oldData.gesture);\n } else if (this.data.handModelStyle === this.str.controller) {\n if (!this.injectedController && this.data.controllerComponent && this.data.webxrControllerProfiles[0]) {\n this.injectRemoteControllerModel();\n }\n if (oldData.controllerEvent && oldData.controllerEvent !== this.data.controllerEvent) {\n var controllerEvent = JSON.parse(this.data.controllerEvent);\n this.el.components[this.data.controllerComponent][controllerEvent.type === 'thumbstickmoved' ? 'onThumbstickMoved' : 'onButtonChanged'](controllerEvent);\n }\n if (oldData.controllerModelUpdate && (oldData.controllerModelUpdate[0] !== this.data.controllerModelUpdate[0] || oldData.controllerModelUpdate[1] !== this.data.controllerModelUpdate[1])) {\n var _this$el$components$t;\n (_this$el$components$t = this.el.components[this.data.controllerComponent]).updateModel.apply(_this$el$components$t, _toConsumableArray(this.data.controllerModelUpdate));\n }\n }\n }\n },\n remove: function remove() {\n this.el.removeObject3D(this.str.mesh);\n },\n addHandModel: function addHandModel() {\n var _this3 = this;\n this.loader = new THREE.GLTFLoader();\n this.loader.setCrossOrigin('anonymous');\n var handmodelUrl = this.MODEL_BASE + this.MODEL_NAMES[this.data.handModelStyle + this.data.hand.charAt(0).toUpperCase() + this.data.hand.slice(1)];\n this.loader.load(this.data.customHandModelURL || handmodelUrl, function (gltf) {\n var newMesh = gltf.scene.children[0];\n var handModelOrientation = _this3.data.hand === 'left' ? Math.PI / 2 : -Math.PI / 2;\n newMesh.mixer = new THREE.AnimationMixer(newMesh);\n _this3.clips = gltf.animations;\n _this3.clips.forEach(function (clip) {\n _this3.clipNameToClip[clip.name] = clip;\n });\n _this3.el.setObject3D(_this3.str.mesh, newMesh);\n var handColor = _this3.data.color;\n newMesh.traverse(function (object) {\n if (!object.isMesh) {\n return;\n }\n object.material.color = new THREE.Color(handColor);\n });\n newMesh.position.set(0, 0, 0);\n newMesh.rotation.set(0, 0, handModelOrientation);\n });\n },\n updateHandMeshColor: function updateHandMeshColor() {\n var mesh = this.getMesh();\n if (!mesh) return;\n var handColor = this.data.color;\n mesh.traverse(function (object) {\n if (!object.isMesh) {\n return;\n }\n object.material.color.set(handColor);\n });\n },\n controllerComponents: ['magicleap-controls', 'vive-controls', 'oculus-touch-controls', 'windows-motion-controls', 'hp-mixed-reality-controls',\n // these were missing from the original hand-controls component:\n 'valve-index-controls',\n // some older models that it doesn't hurt to include:\n 'oculus-go-controls', 'vive-focus-controls'],\n addControllerComponents: function addControllerComponents(useControllerModel) {\n var _this4 = this;\n // this adds all controller components, with wrappers set up to capture and broadcast model found and relevant button presses\n this.controllerComponents.forEach(function (controllerComponentName) {\n if (_this4.data.controllerComponent && _this4.data.controllerComponent !== controllerComponentName) {\n return;\n }\n _this4.el.setAttribute(controllerComponentName, {\n hand: _this4.data.hand,\n model: useControllerModel\n });\n\n // (note: this could possibly be rewritten to rely on the controllerconnected event)\n _this4.el.components[controllerComponentName].injectTrackedControls = _this4.injectControlsWrapper.bind(_this4, _this4.el.components[controllerComponentName].injectTrackedControls.bind(_this4.el.components[controllerComponentName]), controllerComponentName);\n });\n this.isViveController();\n },\n injectControlsWrapper: function injectControlsWrapper(originalFn, controllerComponentName, webxrControllerSymbol) {\n // captures and rebroadcasts the model found by AFRAME.utils.trackedControls.findMatchingControllerWebXR\n this.el.setAttribute('networked-hand-controls', {\n controllerComponent: controllerComponentName,\n webxrControllerProfiles: JSON.stringify(webxrControllerSymbol.profiles)\n });\n originalFn(webxrControllerSymbol);\n\n // this function handles mesh color updates\n this.el.components[controllerComponentName].updateModel = this.updateModelWrapper.bind(this, this.el.components[controllerComponentName].updateModel.bind(this.el.components[controllerComponentName]));\n this.el.components[controllerComponentName].onButtonChanged = this.onButtonChangedWrapper.bind(this, this.el.components[controllerComponentName].onButtonChanged);\n this.el.components[controllerComponentName].onThumbstickMoved = this.onThumbstickMovedWrapper.bind(this, this.el.components[controllerComponentName].onThumbstickMoved);\n\n // we want to strip the listeners pointing to the original functions, and then re-add them with our wrapped functions\n this.el.components[controllerComponentName].removeEventListeners();\n this.el.components[controllerComponentName].addEventListeners();\n },\n onButtonChangedWrapper: function onButtonChangedWrapper(originalFn, evt) {\n this.el.setAttribute('networked-hand-controls', this.str.controllerEvent, JSON.stringify({\n detail: evt.detail,\n type: evt.type\n }));\n originalFn(evt);\n },\n onThumbstickMovedWrapper: function onThumbstickMovedWrapper(originalFn, evt) {\n this.el.setAttribute('networked-hand-controls', this.str.controllerEvent, JSON.stringify({\n detail: evt.detail,\n type: evt.type\n }));\n originalFn(evt);\n },\n updateModelWrapper: function updateModelWrapper(originalFn, buttonName, evtName) {\n // capture and rebroadcast controller events (only used if using controller model instead of hand)\n if (!this.btnEvtMap[buttonName]) {\n this.btnEvtMap[buttonName] = {};\n }\n if (!this.btnEvtMap[buttonName][evtName]) {\n this.btnEvtMap[buttonName][evtName] = \"\".concat(buttonName, \",\").concat(evtName);\n }\n this.el.setAttribute('networked-hand-controls', \"controllerModelUpdate\", this.btnEvtMap[buttonName][evtName]);\n originalFn(buttonName, evtName);\n },\n catchPoseTrackingInitialization: function catchPoseTrackingInitialization() {\n this.catchAndRemoveWebXRTracking = function catchAndRemoveWebXRTracking(evt) {\n if (evt.detail.name !== 'tracked-controls-webxr') {\n return;\n }\n this.el.removeEventListener('componentinitialized', this.catchAndRemoveWebXRTracking);\n this.el.removeAttribute('tracked-controls-webxr');\n }.bind(this);\n this.el.addEventListener('componentinitialized', this.catchAndRemoveWebXRTracking);\n },\n injectRemoteControllerModel: function injectRemoteControllerModel() {\n // this is for a networked controller entity, where we want to show a non-hand controller model\n // we add the relevant controller component, and then hack it just a bit to:\n // A) convince it the model show without relation to whether one is actually locally connected, and \n // B) to not track _local_ pose data (so, no prevent/remove tracked-controller component)\n\n // we pause the element so we can prevent pose tracking from being added.\n this.el.pause();\n\n // we add the actual component, to get the model generated and button meshes and event handling\n this.el.setAttribute(this.data.controllerComponent, {\n hand: this.data.hand,\n model: true\n // could also optionally support custom buttonColor, buttonTouchColor, buttonHighlightColor \n });\n\n // in new design, we leave this, as the controller needs to emit some of these events to itself\n // in order to get mesh color updates\n // this.el.components[this.data.controllerComponent].removeEventListeners();\n\n // we don't want the remote model to listen to local button/trigger/thumbstick events, though \n this.el.components[this.data.controllerComponent].removeControllersUpdateListener();\n\n // some older controller components aren't as well written, and so we have to allow pose tracking to be added and then remove it\n if (!this.el.components[this.data.controllerComponent].checkIfControllerPresent || !this.el.components[this.data.controllerComponent].loadModel) {\n console.warn(\"fallback path: will add and then disable pose tracking\", this.data.controllerComponent, this.el, this.el.components);\n\n // however, we do still want _one_ event listener, so we add the button meshes\n this.el.addEventListener('model-loaded', this.el.components[this.data.controllerComponent].onModelLoaded);\n this.catchPoseTrackingInitialization();\n // here we have no loadModel() to use directly, so we fully inject (bypassing controller detection)\n // and catch and remove pose tracking once it is added.\n this.el.components[this.data.controllerComponent].injectTrackedControls({\n profiles: JSON.parse(this.data.webxrControllerProfiles)\n });\n } else {\n // ideal, default path, for controller components (like oculus-touch-controls), that have separate loadModel() function\n\n // this prevents injectTrackedControllers from running, which prevents the \n // tracked-controls-webxr component from being added, which is responsible for pose tracking\n // (which we are disabling, since we want this controller to follow this NAF entity instead)\n this.el.components[this.data.controllerComponent].checkIfControllerPresent = function (x) {\n return x;\n };\n this.el.components[this.data.controllerComponent].injectTrackedControls = function (x) {\n return x;\n };\n\n // we need to fire this listener to load the button meshes\n this.el.addEventListener('model-loaded', this.el.components[this.data.controllerComponent].onModelLoaded);\n // we load the model indicated by the remote user's headset\n this.el.components[this.data.controllerComponent].loadModel({\n profiles: JSON.parse(this.data.webxrControllerProfiles)\n });\n }\n this.el.play();\n this.injectedController = true;\n },\n getMesh: function getMesh() {\n return this.el.getObject3D(this.str.mesh);\n },\n // local hands are controlled by one's controllers, non-local hands are controlled by updates via NAF\n local: false,\n eventFunctionMap: {\n left: {},\n right: {}\n },\n buttonEventMap: {\n gripdown: ['grip', 'down'],\n gripup: ['grip', 'up'],\n trackpaddown: ['trackpad', 'down'],\n trackpadup: ['trackpad', 'up'],\n trackpadtouchstart: ['trackpad', 'touchstart'],\n trackpadtouchend: ['trackpad', 'touchend'],\n triggerdown: ['trigger', 'down'],\n triggerup: ['trigger', 'up'],\n triggertouchstart: ['trigger', 'touchstart'],\n triggertouchend: ['trigger', 'touchend'],\n griptouchstart: ['grip', 'touchstart'],\n griptouchend: ['grip', 'touchend'],\n abuttontouchstart: ['AorX', 'touchstart'],\n abuttontouchend: ['AorX', 'touchend'],\n bbuttontouchstart: ['BorY', 'touchstart'],\n bbuttontouchend: ['BorY', 'touchend'],\n xbuttontouchstart: ['AorX', 'touchstart'],\n xbuttontouchend: ['AorX', 'touchend'],\n ybuttontouchstart: ['BorY', 'touchstart'],\n ybuttontouchend: ['BorY', 'touchend'],\n surfacetouchstart: ['surface', 'touchstart'],\n surfacetouchend: ['surface', 'touchend']\n\n // these are not used for any existing gestures\n // thumbstickdown: ['thumbstick', 'down'],\n // thumbstickup: ['thumbstick', 'up'], \n },\n visibleListeners: {\n // (note: while `this.el` looks wrong here, we use .bind() on it within init())\n controllerconnected: function controllerconnected() {\n this.el.setAttribute('networked-hand-controls', 'visible', true);\n },\n controllerdisconnected: function controllerdisconnected() {\n this.el.setAttribute('networked-hand-controls', 'visible', false);\n }\n },\n addEventListeners: function addEventListeners() {\n for (var evtName in this.buttonEventMap) {\n this.el.addEventListener(evtName, this.eventFunctionMap[this.data.hand][evtName]);\n }\n for (var _evtName2 in this.visibleListeners) {\n this.el.addEventListener(_evtName2, this.eventFunctionMap[this.data.hand][_evtName2]);\n }\n },\n removeEventListeners: function removeEventListeners() {\n for (var evtName in this.buttonEventMap) {\n this.el.removeEventListener(evtName, this.eventFunctionMap[this.data.hand][evtName]);\n }\n for (var _evtName3 in this.visibleListeners) {\n this.el.removeEventListener(_evtName3, this.eventFunctionMap[this.data.hand][_evtName3]);\n }\n },\n // to minimize garbage collection, prevents generating huge numbers of one-time-use strings\n str: {\n nafHandControls: \"networked-hand-controls\",\n mesh: \"mesh\",\n gesture: \"gesture\",\n down: 'down',\n touchstart: 'touchstart',\n fist: \"fist\",\n point: \"point\",\n thumbUp: \"thumbUp\",\n pistol: \"pistol\",\n hold: \"hold\",\n open: \"open\",\n controller: \"controller\",\n controllerEvent: \"controllerEvent\"\n },\n handleButton: function handleButton(button, evt) {\n // this function is only for generating networked gestures with hand model\n if (this.data.handModelStyle === 'controller') {\n return;\n }\n this.isContact = evt === this.str.down || evt === this.str.touchstart;\n if (this.isContact === this.contact[button]) {\n return;\n }\n this.contact[button] = this.isContact;\n this.lastGesture = this.data.gesture;\n this.determineGesture();\n if (this.gesture === this.lastGesture) {\n return;\n }\n\n // set new gesture into schema to propagate via NAF\n this.el.setAttribute(this.str.nafHandControls, this.str.gesture, this.gesture);\n this.handleGesture();\n },\n handleGesture: function handleGesture() {\n if (this.data.gesture === this.lastGesture) {\n return;\n }\n this.playAnimation();\n this.emitGestureEvents();\n },\n determineGesture: function determineGesture() {\n this.gripIsActive = this.contact.grip;\n this.triggerIsActive = this.contact.trigger;\n this.thumbIsActive = this.contact.trackpad || this.contact.surface;\n this.contact.AorX || this.contact.BorY;\n if (!this.isVive) {\n this.gesture = this.gripIsActive && this.thumbIsActive && this.triggerIsActive ? this.str.fist : this.gripIsActive && this.thumbIsActive && !this.triggerIsActive ? this.str.point : this.gripIsActive && !this.thumbIsActive && this.triggerIsActive ? this.str.thumbUp : this.gripIsActive && !this.thumbIsActive && !this.triggerIsActive ? this.str.pistol : this.triggerIsActive ? this.str.hold : this.str.open;\n } else {\n // Vive handling was supposedly left incomplete\n this.gesture = this.gripIsActive || this.triggerIsActive ? this.str.fist : this.contact.trackpad ? this.str.point : this.str.open;\n }\n },\n emitGestureEvents: function emitGestureEvents() {\n var _this$eventNames$this, _this$eventNames$this2;\n this.el.emit((_this$eventNames$this = this.eventNames[this.lastGesture]) === null || _this$eventNames$this === void 0 ? void 0 : _this$eventNames$this.inactive);\n this.el.emit((_this$eventNames$this2 = this.eventNames[this.data.gesture]) === null || _this$eventNames$this2 === void 0 ? void 0 : _this$eventNames$this2.active);\n },\n // mapping rather than function to reduce garbage collection\n eventNames: {\n fist: {\n active: \"fiststart\",\n inactive: \"fistend\"\n },\n thumbUp: {\n active: \"thumbupstart\",\n inactive: \"thumbupend\"\n },\n point: {\n active: \"pointstart\",\n inactive: \"pointend\"\n },\n hold: {\n active: \"holdstart\",\n inactive: \"holdend\"\n },\n open: {\n active: \"openstart\",\n inactive: \"openend\"\n },\n pistol: {\n active: \"pistolstart\",\n inactive: \"pistolend\"\n }\n },\n playAnimation: function playAnimation() {\n // this function seems like it could be improved\n // implementation is from the original hand-controls component\n\n if (!this.getMesh()) {\n return;\n }\n\n // Stop all current animations\n this.getMesh().mixer.stopAllAction();\n\n // Grab clip action\n this.clip = this.clipNameToClip[this.ANIMATIONS[this.data.gesture]];\n this.toAction = this.getMesh().mixer.clipAction(this.clip);\n this.toAction.clampWhenFinished = true;\n this.toAction.loop = THREE.LoopRepeat;\n this.toAction.repetitions = 0;\n this.toAction.timeScale = this.reverse ? -1 : 1;\n this.toAction.time = this.reverse ? this.clip.duration : 0;\n this.toAction.weight = 1;\n if (!this.lastGesture || this.data.gesture === this.lastGesture) {\n this.getMesh().mixer.stopAllAction();\n this.toAction.play();\n return;\n }\n\n // Animate or crossfade from gesture to gesture\n this.clip = this.clipNameToClip[this.ANIMATIONS[this.lastGesture]];\n this.fromAction = this.getMesh().mixer.clipAction(this.clip);\n this.fromAction.weight = 0.15;\n this.fromAction.play();\n this.toAction.play();\n this.fromAction.crossFadeTo(this.toAction, 0.15, true);\n },\n // see https://github.com/aframevr/assets\n MODEL_BASE: 'https://cdn.aframe.io/controllers/hands/',\n MODEL_NAMES: {\n toonLeft: 'leftHand.glb',\n toonRight: 'rightHand.glb',\n lowPolyLeft: 'leftHandLow.glb',\n lowPolyRight: 'rightHandLow.glb',\n highPolyLeft: 'leftHandHigh.glb',\n highPolyRight: 'rightHandHigh.glb'\n },\n clipNameToClip: {},\n // map from internal state names to the names of the clips as specified within the models\n ANIMATIONS: {\n open: 'Open',\n point: 'Point',\n pistol: 'Point + Thumb',\n fist: 'Fist',\n hold: 'Hold',\n thumbUp: 'Thumb Up'\n },\n isVive: false,\n isViveController: function isViveController() {\n var controller = this.el.components['tracked-controls'] && this.el.components['tracked-controls'].controller;\n this.isVive = controller && (controller.id && controller.id.indexOf('OpenVR ') === 0 || controller.profiles && controller.profiles[0] && controller.profiles[0] === 'htc-vive');\n },\n btnEvtMap: {},\n // dynamically populated, but actually helpful if prototype is modified\n setup: function setup() {\n // preventing garbage collection by making sure these don't end up as part of the prototype\n this.contact = {\n grip: false,\n trigger: false,\n surface: false,\n trackpad: false,\n AorX: false,\n BorY: false\n };\n this.reverse = false;\n this.isContact = false;\n this.injectedController = false;\n this.lastGesture = \"\";\n this.gesture = \"\";\n this.gripIsActive = false;\n this.triggerIsActive = false;\n this.thumbIsActive = false;\n this.clip = null;\n this.toAction = null;\n this.fromAction = null;\n }\n});\n\n//# sourceURL=webpack://networked-aframe/./src/components/networked-hand-controls.js?"); /***/ }), diff --git a/dist/networked-aframe.min.js b/dist/networked-aframe.min.js index d7ec4775..4ffb8a88 100644 --- a/dist/networked-aframe.min.js +++ b/dist/networked-aframe.min.js @@ -1 +1 @@ -(()=>{var t={80:t=>{"use strict";var e=function(){function t(t,e){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:0,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:.15;!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.state=0,this.buffer=[],this.bufferTime=1e3*n,this.time=0,this.mode=e,this.originFrame=i(),this.position=new THREE.Vector3,this.quaternion=new THREE.Quaternion,this.scale=new THREE.Vector3(1,1,1)}return e(t,[{key:"hermite",value:function(t,e,n,i,o,r){var s=e*e,a=e*e*e,c=2*a-3*s+1,u=-2*a+3*s,l=a-2*s+e,h=a-s;t.copy(n.multiplyScalar(c)),t.add(i.multiplyScalar(u)),t.add(o.multiplyScalar(l)),t.add(r.multiplyScalar(h))}},{key:"lerp",value:function(t,e,n,i){t.lerpVectors(e,n,i)}},{key:"slerp",value:function(t,e,n,i){t.slerpQuaternions(e,n,i)}},{key:"updateOriginFrameToBufferTail",value:function(){var t;t=this.originFrame,n.push(t),this.originFrame=this.buffer.shift()}},{key:"appendBuffer",value:function(t,e,n,o){var r=this.buffer.length>0?this.buffer[this.buffer.length-1]:null;if(r&&r.time===this.time)t&&r.position.copy(t),e&&r.velocity.copy(e),n&&r.quaternion.copy(n),o&&r.scale.copy(o);else{var s=r||this.originFrame,a=i();a.position.copy(t||s.position),a.velocity.copy(e||s.velocity),a.quaternion.copy(n||s.quaternion),a.scale.copy(o||s.scale),a.time=this.time,this.buffer.push(a)}}},{key:"setTarget",value:function(t,e,n,i){this.appendBuffer(t,e,n,i)}},{key:"setPosition",value:function(t,e){this.appendBuffer(t,e,null,null)}},{key:"setQuaternion",value:function(t){this.appendBuffer(null,null,t,null)}},{key:"setScale",value:function(t){this.appendBuffer(null,null,null,t)}},{key:"update",value:function(t){if(0===this.state&&this.buffer.length>0&&(this.updateOriginFrameToBufferTail(),this.position.copy(this.originFrame.position),this.quaternion.copy(this.originFrame.quaternion),this.scale.copy(this.originFrame.scale),this.state=1),1===this.state&&this.buffer.length>0&&this.time>this.bufferTime&&(this.state=2),2===this.state){for(var e=this.time-this.bufferTime;this.buffer.length>0&&e>this.buffer[0].time;)this.buffer.length>1?this.updateOriginFrameToBufferTail():(this.originFrame.position.copy(this.buffer[0].position),this.originFrame.velocity.copy(this.buffer[0].velocity),this.originFrame.quaternion.copy(this.buffer[0].quaternion),this.originFrame.scale.copy(this.buffer[0].scale),this.originFrame.time=this.buffer[0].time,this.buffer[0].time=this.time+t);if(this.buffer.length>0&&this.buffer[0].time>0){var n=this.buffer[0],i=n.time-this.originFrame.time,o=(e-this.originFrame.time)/i;0===this.mode?this.lerp(this.position,this.originFrame.position,n.position,o):1===this.mode&&this.hermite(this.position,o,this.originFrame.position,n.position,this.originFrame.velocity.multiplyScalar(i),n.velocity.multiplyScalar(i)),this.slerp(this.quaternion,this.originFrame.quaternion,n.quaternion,o),this.lerp(this.scale,this.originFrame.scale,n.scale,o)}}0!==this.state&&(this.time+=t)}},{key:"getPosition",value:function(){return this.position}},{key:"getQuaternion",value:function(){return this.quaternion}},{key:"getScale",value:function(){return this.scale}}]),t}();t.exports=o},325:t=>{function e(t){return e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},e(t)}function n(t,e){for(var n=0;n{"use strict";function e(t){return e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},e(t)}var n=Array.isArray,i=Object.keys,o=Object.prototype.hasOwnProperty;t.exports=function t(r,s){if(r===s)return!0;if(r&&s&&"object"==e(r)&&"object"==e(s)){var a,c,u,l=n(r),h=n(s);if(l&&h){if((c=r.length)!=s.length)return!1;for(a=c;0!=a--;)if(!t(r[a],s[a]))return!1;return!0}if(l!=h)return!1;var d=r instanceof Date,f=s instanceof Date;if(d!=f)return!1;if(d&&f)return r.getTime()==s.getTime();var p=r instanceof RegExp,m=s instanceof RegExp;if(p!=m)return!1;if(p&&m)return r.toString()==s.toString();var v=i(r);if((c=v.length)!==i(s).length)return!1;for(a=c;0!=a--;)if(!o.call(s,v[a]))return!1;for(a=c;0!=a--;)if(!t(r[u=v[a]],s[u]))return!1;return!0}return r!=r&&s!=s}},520:(t,e,n)=>{var i=n(998),o=n(648),r=n(452),s=n(565),a=n(390),c=n(371),u=n(267),l={app:"",room:"",clientId:""};l.options=i,l.utils=o,l.log=new r,l.schemas=new s,l.version="0.12.0",l.adapters=new u;var h=new a,d=new c(h);l.connection=d,l.entities=h,t.exports=window.NAF=l},367:t=>{function e(t){return e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},e(t)}function n(t,e){for(var n=0;n{function e(t){return e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},e(t)}function n(t,e){for(var n=0;n{function e(t){return e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},e(t)}function n(t,e){for(var n=0;n3&&void 0!==arguments[3]&&arguments[3],o=arguments.length>4&&void 0!==arguments[4]&&arguments[4];NAF.app=e,NAF.room=n,this.adapter.setServerUrl(t),this.adapter.setApp(e),this.adapter.setRoom(n);var r={audio:i,video:o,datachannel:!0};return this.adapter.setWebRtcOptions(r),this.adapter.setServerConnectListeners(this.connectSuccess.bind(this),this.connectFailure.bind(this)),this.adapter.setDataChannelListeners(this.dataChannelOpen.bind(this),this.dataChannelClosed.bind(this),this.receivedData.bind(this)),this.adapter.setRoomOccupantListener(this.occupantsReceived.bind(this)),this.adapter.connect()}},{key:"onConnect",value:function(t){this.onConnectCallback=t,this.isConnected()?t():document.body.addEventListener("connected",t,!1)}},{key:"connectSuccess",value:function(t){NAF.log.write("Networked-Aframe Client ID:",t),NAF.clientId=t;var e=new CustomEvent("connected",{detail:{clientId:t}});document.body.dispatchEvent(e)}},{key:"connectFailure",value:function(t,e){NAF.log.error(t,"failure to connect")}},{key:"occupantsReceived",value:function(t){var e=Object.assign({},this.connectedClients);this.connectedClients=t,this.checkForDisconnectingClients(e,t),this.checkForConnectingClients(t)}},{key:"checkForDisconnectingClients",value:function(t,e){for(var n in t)e[n]||(NAF.log.write("Closing stream to",n),this.adapter.closeStreamConnection(n))}},{key:"checkForConnectingClients",value:function(t){for(var e in t)this.isNewClient(e)&&this.adapter.shouldStartConnectionTo(t[e])&&(NAF.log.write("Opening datachannel to",e),this.adapter.startStreamConnection(e))}},{key:"getConnectedClients",value:function(){return this.connectedClients}},{key:"isConnected",value:function(){return!!NAF.clientId}},{key:"isMineAndConnected",value:function(t){return this.isConnected()&&NAF.clientId===t}},{key:"isNewClient",value:function(t){return!this.isConnectedTo(t)}},{key:"isConnectedTo",value:function(t){return this.adapter.getConnectStatus(t)===NAF.adapters.IS_CONNECTED}},{key:"dataChannelOpen",value:function(t){NAF.log.write("Opened data channel from "+t),this.activeDataChannels[t]=!0,this.entities.completeSync(t,!0);var e=new CustomEvent("clientConnected",{detail:{clientId:t}});document.body.dispatchEvent(e)}},{key:"dataChannelClosed",value:function(t){NAF.log.write("Closed data channel from "+t),this.activeDataChannels[t]=!1,this.entities.removeEntitiesOfClient(t);var e=new CustomEvent("clientDisconnected",{detail:{clientId:t}});document.body.dispatchEvent(e)}},{key:"hasActiveDataChannel",value:function(t){return!!this.activeDataChannels[t]}},{key:"broadcastData",value:function(t,e){this.adapter.broadcastData(t,e)}},{key:"broadcastDataGuaranteed",value:function(t,e){this.adapter.broadcastDataGuaranteed(t,e)}},{key:"sendData",value:function(t,e,n,i){this.hasActiveDataChannel(t)&&(i?this.adapter.sendDataGuaranteed(t,e,n):this.adapter.sendData(t,e,n))}},{key:"sendDataGuaranteed",value:function(t,e,n){this.sendData(t,e,n,!0)}},{key:"subscribeToDataChannel",value:function(t,e){this.isReservedDataType(t)?NAF.log.error("NetworkConnection@subscribeToDataChannel: "+t+" is a reserved dataType. Choose another"):this.dataChannelSubs[t]=e}},{key:"unsubscribeToDataChannel",value:function(t){this.isReservedDataType(t)?NAF.log.error("NetworkConnection@unsubscribeToDataChannel: "+t+" is a reserved dataType. Choose another"):delete this.dataChannelSubs[t]}},{key:"isReservedDataType",value:function(t){return"u"==t||"r"==t}},{key:"receivedData",value:function(t,e,n,i){this.dataChannelSubs[e]?this.dataChannelSubs[e](t,e,n,i):NAF.log.write("NetworkConnection@receivedData: "+e+" has not been subscribed to yet. Call subscribeToDataChannel()")}},{key:"getServerTime",value:function(){return this.adapter.getServerTime()}},{key:"disconnect",value:function(){this.entities.removeRemoteEntities(),this.adapter&&this.adapter.disconnect(),NAF.app="",NAF.room="",NAF.clientId="",this.connectedClients={},this.activeDataChannels={},this.adapter=null,this.setupDefaultDataSubscriptions(),document.body.removeEventListener("connected",this.onConnectCallback)}}],i&&n(e.prototype,i),Object.defineProperty(e,"prototype",{writable:!1}),t}();t.exports=o},390:(t,e,n)=>{function i(t){return i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},i(t)}function o(t,e){for(var n=0;n{function e(t){return e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},e(t)}function n(t,e){for(var n=0;n is defined."));if(!this.validateTemplate(t,e))return;this.templateCache[t.template]=document.importNode(e.content,!0)}else NAF.log.error("Schema not valid: ",t),NAF.log.error("See https://github.com/networked-aframe/networked-aframe#syncing-custom-components")}},{key:"getCachedTemplate",value:function(t){return this.templateIsCached(t)||(this.templateExistsInScene(t)?this.add(this.createDefaultSchema(t)):NAF.log.error("Template el for ".concat(t," is not in the scene, add the template to and register with NAF.schemas.add."))),this.templateCache[t].firstElementChild.cloneNode(!0)}},{key:"templateIsCached",value:function(t){return!!this.templateCache[t]}},{key:"getComponents",value:function(t){var e=["position","rotation"];return this.hasTemplate(t)&&(e=this.schemaDict[t].components),e}},{key:"hasTemplate",value:function(t){return!!this.schemaDict[t]}},{key:"templateExistsInScene",value:function(t){var e=document.querySelector(t);return e&&this.isTemplateTag(e)}},{key:"validateSchema",value:function(t){return!(!t.template||!t.components)}},{key:"validateTemplate",value:function(t,e){return this.isTemplateTag(e)?!!this.templateHasOneOrZeroChildren(e)||(NAF.log.error("Template for ".concat(t.template," has more than one child. Templates must have one direct child element, no more. Template found:"),e),!1):(NAF.log.error("Template for ".concat(t.template," is not a