diff --git a/dist/aframe-blink-controls.js b/dist/aframe-blink-controls.js index 04448e0..16346e8 100644 --- a/dist/aframe-blink-controls.js +++ b/dist/aframe-blink-controls.js @@ -512,7 +512,7 @@ AFRAME.registerComponent('blink-controls', { this.collisionWorldNormal.copy(collisionNormal) .applyMatrix3(this.collisionObjectNormalMatrix).normalize() const angleNormals = this.referenceNormal.angleTo(this.collisionWorldNormal) - return (THREE.Math.RAD2DEG * angleNormals <= this.data.landingMaxAngle) + return (THREE.MathUtils.RAD2DEG * angleNormals <= this.data.landingMaxAngle) }, // Utils diff --git a/dist/aframe-blink-controls.js.map b/dist/aframe-blink-controls.js.map index b02a531..dcbe1fe 100644 --- a/dist/aframe-blink-controls.js.map +++ b/dist/aframe-blink-controls.js.map @@ -1 +1 @@ -{"version":3,"file":"dist/aframe-blink-controls.js","mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,oBAAoB;AACjC,cAAc,oBAAoB;AAClC,aAAa;AACb,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,2EAA2E;AACzF;AACA;AACA,mBAAmB,4BAA4B;AAC/C;AACA;AACA,iBAAiB,4BAA4B;AAC7C;AACA,oBAAoB,4BAA4B;AAChD,yBAAyB,aAAa;AACtC,iBAAiB,kBAAkB;AACnC,iBAAiB,sCAAsC;AACvD,sBAAsB,sCAAsC;AAC5D,wBAAwB,mCAAmC;AAC3D,yBAAyB,uBAAuB;AAChD,yBAAyB,sBAAsB;AAC/C,gBAAgB,YAAY;AAC5B,yBAAyB,qBAAqB;AAC9C,sBAAsB,gBAAgB;AACtC,qBAAqB,mCAAmC;AACxD,sBAAsB,mCAAmC;AACzD,0BAA0B,qBAAqB;AAC/C,wBAAwB,cAAc;AACtC,qBAAqB,yBAAyB,oBAAoB;AAClE,uBAAuB,iCAAiC;AACxD,yBAAyB,eAAe;AACxC,yBAAyB,cAAc;AACvC,mBAAmB,cAAc;AACjC,kBAAkB,cAAc;AAChC,gBAAgB,eAAe;AAC/B,wBAAwB;AACxB,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kBAAkB,kCAAkC;AACpD;AACA;AACA;AACA,kBAAkB,gCAAgC;AAClD;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA,gBAAgB,mCAAmC;AACnD;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,QAAQ;AACR;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,QAAQ;AACR;AACA;AACA,QAAQ;AACR;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA,4BAA4B;AAC5B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,uCAAuC;AACvC,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB,0BAA0B;AAC1B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,0BAA0B;AAC1B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,oFAAoF;AACpF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,QAAQ;AACR;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,sBAAsB,mBAAmB;AACzC;AACA;AACA;AACA,UAAU;AACV;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,uDAAuD;AACvD;AACA;AACA;AACA,4CAA4C,yBAAyB;AACrE;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,4DAA4D;AAC5D;AACA;AACA;AACA;AACA;AACA;AACA,4DAA4D;AAC5D;AACA,0BAA0B;AAC1B;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,0BAA0B;AAC1B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,wBAAwB,kBAAkB;AAC1C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA,aAAa,QAAQ;AACrB,aAAa,QAAQ;AACrB,aAAa,QAAQ;AACrB,eAAe,SAAS;AACxB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA,OAAO,wBAAwB,UAAU;AACzC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,sBAAsB,yBAAyB;AAC/C;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL,qCAAqC,mBAAmB;AACxD;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA,wCAAwC,2CAA2C;AACnF;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA,uCAAuC,iDAAiD;AACxF,uCAAuC,qBAAqB;AAC5D;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA,mDAAmD,iBAAiB;AACpE;AACA,GAAG;AACH,uCAAuC;AACvC,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH","sources":["webpack://aframe-blink-controls/./src/index.js"],"sourcesContent":["/* global THREE, AFRAME */\r\n\r\n// Adapted from https://github.com/fernandojsg/aframe-teleport-controls\r\n// Additions: Teleport rotation, parabolic root calculation, bindings, fix for triangle strip draw mode\r\n// Removals: Line teleport\r\n// WARNING: Super early! Currently only tested with Oculus Touch controllers\r\n\r\nAFRAME.registerGeometry('prism', {\r\n schema: {\r\n depth: { default: 1, min: 0 },\r\n height: { default: 1, min: 0 },\r\n width: { default: 1, min: 0 }\r\n },\r\n\r\n init: function (data) {\r\n const shape = new THREE.Shape()\r\n shape.moveTo(data.width / 2, 0)\r\n shape.lineTo(0, data.height)\r\n shape.lineTo(-data.width / 2, 0)\r\n shape.lineTo(data.width / 2, 0)\r\n\r\n const extrudeSettings = {\r\n steps: 2,\r\n depth: data.depth,\r\n bevelEnabled: false\r\n }\r\n this.geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings)\r\n }\r\n})\r\n\r\n// WIP: Controller bindings cheat sheet\r\n// For HTC Vive: trackpaddown and trackpadup with axismove\r\n// For Oculus Touch: thumbstickdown and thumbstickup, with thumbstick event and evt.detail.y and evt.detail.x\r\n// For Valve Index (maybe): touchstart, touchend, axismove?\r\n\r\nif (typeof AFRAME === 'undefined') {\r\n throw new Error('Component attempted to register before AFRAME was available.')\r\n}\r\n\r\nAFRAME.registerComponent('blink-controls', {\r\n schema: {\r\n // Button is a simplified startEvents & endEvents specification, e.g.\r\n // 'thumbstick' binds 'thumbstickdown' and 'thumbstickup' respectively\r\n button: { default: '', oneOf: ['trackpad', 'trigger', 'grip', 'menu', 'thumbstick'] },\r\n // The default teleport activation is a forward thumbstick axis,\r\n // but this can be changed with startEvents.\r\n startEvents: { type: 'array', default: [] },\r\n // The default teleport de-activation is a centered thumbstick axis,\r\n // but this can be changed with endEvents.\r\n endEvents: { type: 'array', default: [] },\r\n // Not assigned by default\r\n cancelEvents: { type: 'array', default: [] },\r\n collisionEntities: { default: '' },\r\n hitEntity: { type: 'selector' },\r\n cameraRig: { type: 'selector', default: '#player' },\r\n teleportOrigin: { type: 'selector', default: '#camera' },\r\n hitCylinderColor: { type: 'color', default: '#4d93fd' },\r\n hitCylinderRadius: { default: 0.25, min: 0 },\r\n hitCylinderHeight: { default: 0.3, min: 0 },\r\n interval: { default: 0 },\r\n curveNumberPoints: { default: 60, min: 2 },\r\n curveLineWidth: { default: 0.025 },\r\n curveHitColor: { type: 'color', default: '#4d93fd' },\r\n curveMissColor: { type: 'color', default: '#ff0000' },\r\n curveShootingSpeed: { default: 10, min: 0 },\r\n defaultPlaneSize: { default: 100 },\r\n landingNormal: { type: 'vec3', default: { x: 0, y: 1, z: 0 } },\r\n landingMaxAngle: { default: '45', min: 0, max: 360 },\r\n drawIncrementally: { default: true },\r\n incrementalDrawMs: { default: 300 },\r\n missOpacity: { default: 0.8 },\r\n hitOpacity: { default: 0.8 },\r\n snapTurn: { default: true },\r\n rotateOnTeleport: { default: true }\r\n },\r\n\r\n init: function () {\r\n const data = this.data\r\n const el = this.el\r\n let i\r\n\r\n this.active = false\r\n this.obj = el.object3D\r\n this.controllerPosition = new THREE.Vector3()\r\n this.hitEntityQuaternion = new THREE.Quaternion()\r\n // teleportOrigin is headset/camera with look-controls\r\n this.teleportOriginQuaternion = new THREE.Quaternion()\r\n this.hitPoint = new THREE.Vector3()\r\n this.collisionObjectNormalMatrix = new THREE.Matrix3()\r\n this.collisionWorldNormal = new THREE.Vector3()\r\n this.rigWorldPosition = new THREE.Vector3()\r\n this.newRigWorldPosition = new THREE.Vector3()\r\n this.teleportEventDetail = {\r\n oldPosition: this.rigWorldPosition,\r\n newPosition: this.newRigWorldPosition,\r\n hitPoint: this.hitPoint,\r\n rotationQuaternion: this.hitEntityQuaternion\r\n }\r\n\r\n this.hit = false\r\n this.prevCheckTime = undefined\r\n this.referenceNormal = new THREE.Vector3()\r\n this.curveMissColor = new THREE.Color()\r\n this.curveHitColor = new THREE.Color()\r\n this.raycaster = new THREE.Raycaster()\r\n\r\n this.defaultPlane = this.createDefaultPlane(this.data.defaultPlaneSize)\r\n this.defaultCollisionMeshes = [this.defaultPlane]\r\n\r\n const teleportEntity = this.teleportEntity = document.createElement('a-entity')\r\n teleportEntity.classList.add('teleportRay')\r\n teleportEntity.setAttribute('visible', false)\r\n el.sceneEl.appendChild(this.teleportEntity)\r\n\r\n this.onButtonDown = this.onButtonDown.bind(this)\r\n this.onButtonUp = this.onButtonUp.bind(this)\r\n this.cancel = this.cancel.bind(this)\r\n this.handleThumbstickAxis = this.handleThumbstickAxis.bind(this)\r\n\r\n this.teleportOrigin = this.data.teleportOrigin\r\n this.cameraRig = this.data.cameraRig\r\n\r\n this.snapturnRotation = THREE.MathUtils.degToRad(45)\r\n this.canSnapturn = true\r\n this.addedEvents = [];\r\n\r\n // Are startEvents and endEvents specified?\r\n if (this.data.startEvents.length && this.data.endEvents.length) {\r\n for (i = 0; i < this.data.startEvents.length; i++) {\r\n this.addedEvents.push([this.data.startEvents[i], this.onButtonDown])\r\n el.addEventListener(this.data.startEvents[i], this.onButtonDown)\r\n }\r\n for (i = 0; i < this.data.endEvents.length; i++) {\r\n this.addedEvents.push([this.data.endEvents[i], this.onButtonUp])\r\n el.addEventListener(this.data.endEvents[i], this.onButtonUp)\r\n }\r\n // Is a button for activation specified?\r\n } else if (data.button) {\r\n this.addedEvents.push([data.button + 'down', this.onButtonDown])\r\n this.addedEvents.push([data.button + 'up', this.onButtonUp])\r\n el.addEventListener(data.button + 'down', this.onButtonDown)\r\n el.addEventListener(data.button + 'up', this.onButtonUp)\r\n // If none of the above, default to thumbstick-axis based activation\r\n } else {\r\n this.thumbstickAxisActivation = true\r\n }\r\n \r\n for (i = 0; i < this.data.cancelEvents.length; i++) {\r\n this.addedEvents.push([this.data.cancelEvents[i], this.cancel])\r\n el.addEventListener(this.data.cancelEvents[i], this.cancel)\r\n }\r\n\r\n this.addedEvents.push(['thumbstickmoved', this.handleThumbstickAxis])\r\n el.addEventListener('thumbstickmoved', this.handleThumbstickAxis)\r\n this.queryCollisionEntities()\r\n },\r\n handleSnapturn: function (rotation, strength) {\r\n if (strength < 0.50) this.canSnapturn = true\r\n if (!this.canSnapturn) return\r\n // Only do snapturns if axis is very prominent (user intent is clear)\r\n // And preven further snapturns until axis returns to (close enough to) 0\r\n if (strength > 0.95) {\r\n if (Math.abs(rotation - Math.PI / 2.0) < 0.6) {\r\n this.cameraRig.object3D.rotateY(+this.snapturnRotation)\r\n this.canSnapturn = false\r\n } else if (Math.abs(rotation - 1.5 * Math.PI) < 0.6) {\r\n this.cameraRig.object3D.rotateY(-this.snapturnRotation)\r\n this.canSnapturn = false\r\n }\r\n }\r\n // if (rotation ) {\r\n // this.cameraRig.object3D.rotateY(-Math.sign(x) * this.snapturnRotation)\r\n // this.canSnapturn = false\r\n // }\r\n },\r\n handleThumbstickAxis: function (evt) {\r\n if (evt.detail.x !== undefined && evt.detail.y !== undefined) {\r\n const rotation = Math.atan2(evt.detail.x, evt.detail.y) + Math.PI\r\n const strength = Math.sqrt(evt.detail.x ** 2 + evt.detail.y ** 2)\r\n\r\n if (this.active) {\r\n // Only rotate if the axes are sufficiently prominent,\r\n // to prevent rotating in undesired/fluctuating directions.\r\n if (strength > 0.95) {\r\n this.obj.getWorldPosition(this.controllerPosition)\r\n this.controllerPosition.setComponent(1, this.hitEntity.object3D.position.y)\r\n // TODO: We set hitEntity invisible to prevent rotation glitches\r\n // but we could also rotate an invisible object instead and only\r\n // apply the final rotation to hitEntity.\r\n this.hitEntity.object3D.visible = false\r\n this.hitEntity.object3D.lookAt(this.controllerPosition)\r\n this.hitEntity.object3D.rotateY(rotation)\r\n this.hitEntity.object3D.visible = true\r\n this.hitEntity.object3D.getWorldQuaternion(this.hitEntityQuaternion)\r\n }\r\n if (Math.abs(evt.detail.x) === 0 && Math.abs(evt.detail.y) === 0) {\r\n // Disable teleport on axis return to 0 if axis (de)activation is enabled\r\n this.onButtonUp()\r\n }\r\n // Forward (rotation 0.0 || 6.28 is straight ahead)\r\n // We use half a radian left and right for some leeway\r\n // We also check for significant y axis movement to prevent\r\n // accidental teleports\r\n } else if (this.thumbstickAxisActivation && strength > 0.95 && (rotation < 0.50 || rotation > 5.78)) {\r\n // Activate (fuzzily) on forward axis if axis activation is enabled\r\n this.onButtonDown()\r\n } else if (this.data.snapTurn) {\r\n this.handleSnapturn(rotation, strength)\r\n }\r\n }\r\n },\r\n update: function (oldData) {\r\n const data = this.data\r\n const diff = AFRAME.utils.diff(data, oldData)\r\n\r\n // Update normal.\r\n this.referenceNormal.copy(data.landingNormal)\r\n\r\n // Update colors.\r\n this.curveMissColor.set(data.curveMissColor)\r\n this.curveHitColor.set(data.curveHitColor)\r\n\r\n // Create or update line mesh.\r\n if (!this.line ||\r\n 'curveLineWidth' in diff || 'curveNumberPoints' in diff || 'type' in diff) {\r\n this.line = this.createLine(data)\r\n this.line.material.opacity = this.data.hitOpacity\r\n this.line.material.transparent = this.data.hitOpacity < 1\r\n this.numActivePoints = data.curveNumberPoints\r\n this.teleportEntity.setObject3D('mesh', this.line.mesh)\r\n }\r\n\r\n // Create or update hit entity.\r\n if (data.hitEntity) {\r\n this.hitEntity = data.hitEntity\r\n } else if (!this.hitEntity || 'hitCylinderColor' in diff || 'hitCylinderHeight' in diff ||\r\n 'hitCylinderRadius' in diff) {\r\n // Remove previous entity, create new entity (could be more performant).\r\n if (this.hitEntity) { this.hitEntity.parentNode.removeChild(this.hitEntity) }\r\n this.hitEntity = this.createHitEntity(data)\r\n this.el.sceneEl.appendChild(this.hitEntity)\r\n }\r\n this.hitEntity.setAttribute('visible', false)\r\n\r\n // If it has rotation on teleport disabled hide the arrow indicating the teleportation direction \r\n if (!data.hitEntity) {\r\n this.hitEntity.lastElementChild.setAttribute('visible', data.rotateOnTeleport);\r\n }\r\n\r\n if ('collisionEntities' in diff) { this.queryCollisionEntities() }\r\n },\r\n\r\n remove: function () {\r\n const el = this.el\r\n const hitEntity = this.hitEntity\r\n const teleportEntity = this.teleportEntity\r\n\r\n if (hitEntity) { hitEntity.parentNode.removeChild(hitEntity) }\r\n if (teleportEntity) { teleportEntity.parentNode.removeChild(teleportEntity) }\r\n\r\n el.sceneEl.removeEventListener('child-attached', this.childAttachHandler)\r\n el.sceneEl.removeEventListener('child-detached', this.childDetachHandler)\r\n\r\n // Clean up event listeners if component removed but element isn't\r\n for (const [name, fn] of this.addedEvents) {\r\n el.removeEventListener(name, fn);\r\n }\r\n },\r\n\r\n tick: (function () {\r\n const p0 = new THREE.Vector3()\r\n const v0 = new THREE.Vector3()\r\n const g = -9.8\r\n const a = new THREE.Vector3(0, g, 0)\r\n const next = new THREE.Vector3()\r\n const last = new THREE.Vector3()\r\n const quaternion = new THREE.Quaternion()\r\n const translation = new THREE.Vector3()\r\n const scale = new THREE.Vector3()\r\n const shootAngle = new THREE.Vector3()\r\n const lastNext = new THREE.Vector3()\r\n const auxDirection = new THREE.Vector3()\r\n let timeSinceDrawStart = 0\r\n\r\n return function (time, delta) {\r\n if (!this.active) { return }\r\n if (this.data.drawIncrementally && this.redrawLine) {\r\n this.redrawLine = false\r\n timeSinceDrawStart = 0\r\n }\r\n timeSinceDrawStart += delta\r\n this.numActivePoints = this.data.curveNumberPoints * timeSinceDrawStart / this.data.incrementalDrawMs\r\n if (this.numActivePoints > this.data.curveNumberPoints) {\r\n this.numActivePoints = this.data.curveNumberPoints\r\n }\r\n\r\n // Only check for intersection if interval time has passed.\r\n if (this.prevCheckTime && (time - this.prevCheckTime < this.data.interval)) { return }\r\n // Update check time.\r\n this.prevCheckTime = time\r\n\r\n const matrixWorld = this.obj.matrixWorld\r\n matrixWorld.decompose(translation, quaternion, scale)\r\n\r\n const direction = shootAngle.set(0, 0, -1)\r\n .applyQuaternion(quaternion).normalize()\r\n this.line.setDirection(auxDirection.copy(direction))\r\n this.obj.getWorldPosition(p0)\r\n\r\n last.copy(p0)\r\n\r\n // Set default status as non-hit\r\n this.teleportEntity.setAttribute('visible', true)\r\n\r\n // But use hit color until ray animation finishes\r\n if (timeSinceDrawStart < this.data.incrementalDrawMs) {\r\n this.line.material.color.set(this.curveHitColor)\r\n } else {\r\n this.line.material.color.set(this.curveMissColor)\r\n }\r\n this.line.material.opacity = this.data.missOpacity\r\n this.line.material.transparent = this.data.missOpacity < 1\r\n this.hitEntity.setAttribute('visible', false)\r\n this.hit = false\r\n\r\n v0.copy(direction).multiplyScalar(this.data.curveShootingSpeed)\r\n\r\n this.lastDrawnIndex = 0\r\n const numPoints = this.data.drawIncrementally ? this.numActivePoints : this.line.numPoints\r\n for (let i = 0; i < numPoints + 1; i++) {\r\n let t\r\n if (i === Math.floor(numPoints + 1)) {\r\n t = numPoints / (this.line.numPoints - 1)\r\n } else {\r\n t = i / (this.line.numPoints - 1)\r\n }\r\n const timeToReach0 = this.parabolicCurveMaxRoot(p0, v0, a)\r\n t = t * Math.max(1, 1.5 * timeToReach0)\r\n\r\n this.parabolicCurve(p0, v0, a, t, next)\r\n // Update the raycaster with the length of the current segment last->next\r\n const dirLastNext = lastNext.copy(next).sub(last).normalize()\r\n this.raycaster.far = dirLastNext.length()\r\n this.raycaster.set(last, dirLastNext)\r\n\r\n this.lastDrawnPoint = next\r\n this.lastDrawnIndex = i\r\n if (this.checkMeshCollisions(i, last, next)) { break }\r\n\r\n last.copy(next)\r\n }\r\n for (let j = this.lastDrawnIndex + 1; j < this.line.numPoints; j++) {\r\n this.line.setPoint(j, this.lastDrawnPoint, this.lastDrawnPoint)\r\n }\r\n }\r\n })(),\r\n\r\n /**\r\n * Run `querySelectorAll` for `collisionEntities` and maintain it with `child-attached`\r\n * and `child-detached` events.\r\n */\r\n queryCollisionEntities: function () {\r\n const data = this.data\r\n const el = this.el\r\n\r\n if (!data.collisionEntities) {\r\n this.collisionEntities = []\r\n return\r\n }\r\n\r\n const collisionEntities = [].slice.call(el.sceneEl.querySelectorAll(data.collisionEntities))\r\n this.collisionEntities = collisionEntities\r\n\r\n // Update entity list on attach.\r\n this.childAttachHandler = function childAttachHandler (evt) {\r\n if (!evt.detail.el.matches(data.collisionEntities)) { return }\r\n collisionEntities.push(evt.detail.el)\r\n }\r\n el.sceneEl.addEventListener('child-attached', this.childAttachHandler)\r\n\r\n // Update entity list on detach.\r\n this.childDetachHandler = function childDetachHandler (evt) {\r\n if (!evt.detail.el.matches(data.collisionEntities)) { return }\r\n const index = collisionEntities.indexOf(evt.detail.el)\r\n if (index === -1) { return }\r\n collisionEntities.splice(index, 1)\r\n }\r\n el.sceneEl.addEventListener('child-detached', this.childDetachHandler)\r\n },\r\n\r\n onButtonDown: function () {\r\n this.active = true\r\n this.redrawLine = true\r\n },\r\n\r\n /**\r\n * Jump!\r\n */\r\n onButtonUp: (function () {\r\n const newRigLocalPosition = new THREE.Vector3()\r\n const newHandPosition = [new THREE.Vector3(), new THREE.Vector3()] // Left and right\r\n const handPosition = new THREE.Vector3()\r\n\r\n return function (evt) {\r\n if (!this.active) { return }\r\n\r\n // Hide the hit point and the curve\r\n this.active = false\r\n this.hitEntity.setAttribute('visible', false)\r\n this.teleportEntity.setAttribute('visible', false)\r\n\r\n if (!this.hit) {\r\n // Button released but no hit point\r\n return\r\n }\r\n\r\n const rig = this.data.cameraRig || this.el.sceneEl.camera.el\r\n rig.object3D.getWorldPosition(this.rigWorldPosition)\r\n this.newRigWorldPosition.copy(this.hitPoint)\r\n\r\n // Finally update the rigs position\r\n newRigLocalPosition.copy(this.newRigWorldPosition)\r\n if (rig.object3D.parent) {\r\n rig.object3D.parent.worldToLocal(newRigLocalPosition)\r\n }\r\n rig.setAttribute('position', newRigLocalPosition)\r\n\r\n // Also take the headset/camera rotation itself into account\r\n if (this.data.rotateOnTeleport) {\r\n this.teleportOriginQuaternion\r\n .setFromEuler(new THREE.Euler(0, this.teleportOrigin.object3D.rotation.y, 0))\r\n this.teleportOriginQuaternion.invert()\r\n this.teleportOriginQuaternion.multiply(this.hitEntityQuaternion)\r\n // Rotate the rig based on calculated teleport origin rotation\r\n this.cameraRig.object3D.setRotationFromQuaternion(this.teleportOriginQuaternion)\r\n }\r\n\r\n // If a rig was not explicitly declared, look for hands and move them proportionally as well\r\n if (!this.data.cameraRig) {\r\n const hands = document.querySelectorAll('a-entity[tracked-controls]')\r\n for (let i = 0; i < hands.length; i++) {\r\n hands[i].object3D.getWorldPosition(handPosition)\r\n\r\n // diff = rigWorldPosition - handPosition\r\n // newPos = newRigWorldPosition - diff\r\n newHandPosition[i].copy(this.newRigWorldPosition).sub(this.rigWorldPosition).add(handPosition)\r\n hands[i].setAttribute('position', newHandPosition[i])\r\n }\r\n }\r\n\r\n this.el.emit('teleported', this.teleportEventDetail)\r\n }\r\n })(),\r\n\r\n cancel: function () {\r\n this.active = false\r\n this.hitEntity.setAttribute('visible', false)\r\n this.teleportEntity.setAttribute('visible', false)\r\n },\r\n\r\n /**\r\n * Check for raycaster intersection.\r\n *\r\n * @param {number} Line fragment point index.\r\n * @param {number} Last line fragment point index.\r\n * @param {number} Next line fragment point index.\r\n * @returns {boolean} true if there's an intersection.\r\n */\r\n checkMeshCollisions: function (i, last, next) {\r\n // @todo We should add a property to define if the collisionEntity is dynamic or static\r\n // If static we should do the map just once, otherwise we're recreating the array in every\r\n // loop when aiming.\r\n let meshes\r\n if (!this.data.collisionEntities) {\r\n meshes = this.defaultCollisionMeshes\r\n } else {\r\n meshes = this.collisionEntities.map(function (entity) {\r\n return entity.getObject3D('mesh')\r\n }).filter(function (n) { return n })\r\n meshes = meshes.length ? meshes : this.defaultCollisionMeshes\r\n }\r\n\r\n const intersects = this.raycaster.intersectObjects(meshes, true)\r\n if (intersects.length > 0 && !this.hit &&\r\n this.isValidNormalsAngle(intersects[0].face.normal, intersects[0].object)) {\r\n const point = intersects[0].point\r\n\r\n this.line.material.color.set(this.curveHitColor)\r\n this.line.material.opacity = this.data.hitOpacity\r\n this.line.material.transparent = this.data.hitOpacity < 1\r\n this.hitEntity.setAttribute('position', point)\r\n this.hitEntity.setAttribute('visible', true)\r\n\r\n this.hit = true\r\n this.hitPoint.copy(intersects[0].point)\r\n\r\n // If hit, just fill the rest of the points with the hit point and break the loop\r\n for (let j = i; j < this.line.numPoints; j++) {\r\n this.line.setPoint(j, last, this.hitPoint)\r\n }\r\n return true\r\n } else {\r\n this.line.setPoint(i, last, next)\r\n return false\r\n }\r\n },\r\n\r\n isValidNormalsAngle: function (collisionNormal, collisionObject) {\r\n this.collisionObjectNormalMatrix.getNormalMatrix(collisionObject.matrixWorld)\r\n this.collisionWorldNormal.copy(collisionNormal)\r\n .applyMatrix3(this.collisionObjectNormalMatrix).normalize()\r\n const angleNormals = this.referenceNormal.angleTo(this.collisionWorldNormal)\r\n return (THREE.Math.RAD2DEG * angleNormals <= this.data.landingMaxAngle)\r\n },\r\n\r\n // Utils\r\n // Parabolic motion equation, y = p0 + v0*t + 1/2at^2\r\n parabolicCurveScalar: function (p0, v0, a, t) {\r\n return p0 + v0 * t + 0.5 * a * t * t\r\n },\r\n\r\n // Parabolic motion equation applied to 3 dimensions\r\n parabolicCurve: function (p0, v0, a, t, out) {\r\n out.x = this.parabolicCurveScalar(p0.x, v0.x, a.x, t)\r\n out.y = this.parabolicCurveScalar(p0.y, v0.y, a.y, t)\r\n out.z = this.parabolicCurveScalar(p0.z, v0.z, a.z, t)\r\n return out\r\n },\r\n\r\n // To determine how long in terms of t we need to calculate\r\n parabolicCurveMaxRoot: function (p0, v0, a) {\r\n const root = (-v0.y - Math.sqrt(v0.y ** 2 - 4 * (0.5 * a.y) * p0.y)) / (2 * 0.5 * a.y)\r\n return root\r\n },\r\n\r\n createLine: function (data) {\r\n const numPoints = data.type === 'line' ? 2 : data.curveNumberPoints\r\n return new AFRAME.utils.RayCurve(numPoints, data.curveLineWidth)\r\n },\r\n\r\n /**\r\n * Create mesh to represent the area of intersection.\r\n * Default to a combination of torus and cylinder.\r\n */\r\n createHitEntity: function (data) {\r\n // Parent.\r\n const hitEntity = document.createElement('a-entity')\r\n hitEntity.className = 'hitEntity'\r\n\r\n // Torus.\r\n const torus = document.createElement('a-entity')\r\n torus.setAttribute('geometry', {\r\n primitive: 'torus',\r\n radius: data.hitCylinderRadius,\r\n radiusTubular: 0.01\r\n })\r\n torus.setAttribute('rotation', { x: 90, y: 0, z: 0 })\r\n torus.setAttribute('material', {\r\n shader: 'flat',\r\n color: data.hitCylinderColor,\r\n side: 'double',\r\n depthTest: false\r\n })\r\n hitEntity.appendChild(torus)\r\n\r\n // Cylinder.\r\n const cylinder = document.createElement('a-entity')\r\n cylinder.setAttribute('position', { x: 0, y: data.hitCylinderHeight / 2, z: 0 })\r\n cylinder.setAttribute('geometry', {\r\n primitive: 'cylinder',\r\n segmentsHeight: 1,\r\n radius: data.hitCylinderRadius,\r\n height: data.hitCylinderHeight,\r\n openEnded: true\r\n })\r\n cylinder.setAttribute('material', {\r\n shader: 'flat',\r\n color: data.hitCylinderColor,\r\n opacity: 0.5,\r\n side: 'double',\r\n src: this.cylinderTexture,\r\n transparent: true,\r\n depthTest: false\r\n })\r\n hitEntity.appendChild(cylinder)\r\n\r\n const pointer = document.createElement('a-entity')\r\n pointer.setAttribute('position', { x: 0, y: 0.05, z: data.hitCylinderRadius * -1.5 })\r\n pointer.setAttribute('rotation', { x: 90, y: 180, z: 0 })\r\n pointer.setAttribute('geometry', {\r\n primitive: 'prism',\r\n height: 0.2,\r\n width: 0.2,\r\n depth: 0.05\r\n })\r\n pointer.setAttribute('material', {\r\n shader: 'flat',\r\n color: data.hitCylinderColor,\r\n side: 'double',\r\n transparent: true,\r\n opacity: 0.6,\r\n depthTest: false\r\n })\r\n hitEntity.appendChild(pointer)\r\n\r\n return hitEntity\r\n },\r\n createDefaultPlane: function (size) {\r\n const geometry = new THREE.PlaneGeometry(100, 100)\r\n geometry.rotateX(-Math.PI / 2)\r\n const material = new THREE.MeshBasicMaterial({ color: 0xffff00 })\r\n return new THREE.Mesh(geometry, material)\r\n },\r\n cylinderTexture: 'url(data:image/png;base64,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)'\r\n})\r\n\r\nAFRAME.utils.RayCurve = function (numPoints, width) {\r\n this.geometry = new THREE.BufferGeometry()\r\n this.vertices = new Float32Array(numPoints * 3 * 6) // 6 vertices (2 triangles) * 3 dimensions\r\n this.uvs = new Float32Array(numPoints * 2 * 6) // 2 uvs per vertex\r\n this.width = width\r\n\r\n this.geometry.setAttribute('position', new THREE.BufferAttribute(this.vertices, 3).setUsage(THREE.DynamicDrawUsage))\r\n\r\n this.material = new THREE.MeshBasicMaterial({\r\n side: THREE.DoubleSide,\r\n color: 0xff0000\r\n })\r\n\r\n this.mesh = new THREE.Mesh(this.geometry, this.material)\r\n\r\n this.mesh.frustumCulled = false\r\n this.mesh.vertices = this.vertices\r\n\r\n this.direction = new THREE.Vector3()\r\n this.numPoints = numPoints\r\n}\r\n\r\nAFRAME.utils.RayCurve.prototype = {\r\n setDirection: function (direction) {\r\n const UP = new THREE.Vector3(0, 1, 0)\r\n this.direction\r\n .copy(direction)\r\n .cross(UP)\r\n .normalize()\r\n .multiplyScalar(this.width / 2)\r\n },\r\n\r\n setWidth: function (width) {\r\n this.width = width\r\n },\r\n\r\n setPoint: (function () {\r\n const posA = new THREE.Vector3()\r\n const posB = new THREE.Vector3()\r\n const posC = new THREE.Vector3()\r\n const posD = new THREE.Vector3()\r\n\r\n return function (i, last, next) {\r\n posA.copy(last).add(this.direction)\r\n posB.copy(last).sub(this.direction)\r\n\r\n posC.copy(next).add(this.direction)\r\n posD.copy(next).sub(this.direction)\r\n\r\n let idx = 6 * 3 * i // 6 vertices per point\r\n\r\n this.vertices[idx++] = posA.x\r\n this.vertices[idx++] = posA.y\r\n this.vertices[idx++] = posA.z\r\n\r\n this.vertices[idx++] = posB.x\r\n this.vertices[idx++] = posB.y\r\n this.vertices[idx++] = posB.z\r\n\r\n this.vertices[idx++] = posC.x\r\n this.vertices[idx++] = posC.y\r\n this.vertices[idx++] = posC.z\r\n\r\n this.vertices[idx++] = posC.x\r\n this.vertices[idx++] = posC.y\r\n this.vertices[idx++] = posC.z\r\n\r\n this.vertices[idx++] = posB.x\r\n this.vertices[idx++] = posB.y\r\n this.vertices[idx++] = posB.z\r\n\r\n this.vertices[idx++] = posD.x\r\n this.vertices[idx++] = posD.y\r\n this.vertices[idx++] = posD.z\r\n\r\n this.geometry.attributes.position.needsUpdate = true\r\n }\r\n })()\r\n}\r\n"],"names":[],"sourceRoot":""} \ No newline at end of file +{"version":3,"file":"dist/aframe-blink-controls.js","mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,oBAAoB;AACjC,cAAc,oBAAoB;AAClC,aAAa;AACb,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,2EAA2E;AACzF;AACA;AACA,mBAAmB,4BAA4B;AAC/C;AACA;AACA,iBAAiB,4BAA4B;AAC7C;AACA,oBAAoB,4BAA4B;AAChD,yBAAyB,aAAa;AACtC,iBAAiB,kBAAkB;AACnC,iBAAiB,sCAAsC;AACvD,sBAAsB,sCAAsC;AAC5D,wBAAwB,mCAAmC;AAC3D,yBAAyB,uBAAuB;AAChD,yBAAyB,sBAAsB;AAC/C,gBAAgB,YAAY;AAC5B,yBAAyB,qBAAqB;AAC9C,sBAAsB,gBAAgB;AACtC,qBAAqB,mCAAmC;AACxD,sBAAsB,mCAAmC;AACzD,0BAA0B,qBAAqB;AAC/C,wBAAwB,cAAc;AACtC,qBAAqB,yBAAyB,oBAAoB;AAClE,uBAAuB,iCAAiC;AACxD,yBAAyB,eAAe;AACxC,yBAAyB,cAAc;AACvC,mBAAmB,cAAc;AACjC,kBAAkB,cAAc;AAChC,gBAAgB,eAAe;AAC/B,wBAAwB;AACxB,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kBAAkB,kCAAkC;AACpD;AACA;AACA;AACA,kBAAkB,gCAAgC;AAClD;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA,gBAAgB,mCAAmC;AACnD;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,QAAQ;AACR;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,QAAQ;AACR;AACA;AACA,QAAQ;AACR;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA,4BAA4B;AAC5B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,uCAAuC;AACvC,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB,0BAA0B;AAC1B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,0BAA0B;AAC1B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,oFAAoF;AACpF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,QAAQ;AACR;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,sBAAsB,mBAAmB;AACzC;AACA;AACA;AACA,UAAU;AACV;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,uDAAuD;AACvD;AACA;AACA;AACA,4CAA4C,yBAAyB;AACrE;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,4DAA4D;AAC5D;AACA;AACA;AACA;AACA;AACA;AACA,4DAA4D;AAC5D;AACA,0BAA0B;AAC1B;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,0BAA0B;AAC1B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,wBAAwB,kBAAkB;AAC1C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA,aAAa,QAAQ;AACrB,aAAa,QAAQ;AACrB,aAAa,QAAQ;AACrB,eAAe,SAAS;AACxB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA,OAAO,wBAAwB,UAAU;AACzC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,sBAAsB,yBAAyB;AAC/C;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL,qCAAqC,mBAAmB;AACxD;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA,wCAAwC,2CAA2C;AACnF;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA,uCAAuC,iDAAiD;AACxF,uCAAuC,qBAAqB;AAC5D;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA,mDAAmD,iBAAiB;AACpE;AACA,GAAG;AACH,uCAAuC;AACvC,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH","sources":["webpack://aframe-blink-controls/./src/index.js"],"sourcesContent":["/* global THREE, AFRAME */\r\n\r\n// Adapted from https://github.com/fernandojsg/aframe-teleport-controls\r\n// Additions: Teleport rotation, parabolic root calculation, bindings, fix for triangle strip draw mode\r\n// Removals: Line teleport\r\n// WARNING: Super early! Currently only tested with Oculus Touch controllers\r\n\r\nAFRAME.registerGeometry('prism', {\r\n schema: {\r\n depth: { default: 1, min: 0 },\r\n height: { default: 1, min: 0 },\r\n width: { default: 1, min: 0 }\r\n },\r\n\r\n init: function (data) {\r\n const shape = new THREE.Shape()\r\n shape.moveTo(data.width / 2, 0)\r\n shape.lineTo(0, data.height)\r\n shape.lineTo(-data.width / 2, 0)\r\n shape.lineTo(data.width / 2, 0)\r\n\r\n const extrudeSettings = {\r\n steps: 2,\r\n depth: data.depth,\r\n bevelEnabled: false\r\n }\r\n this.geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings)\r\n }\r\n})\r\n\r\n// WIP: Controller bindings cheat sheet\r\n// For HTC Vive: trackpaddown and trackpadup with axismove\r\n// For Oculus Touch: thumbstickdown and thumbstickup, with thumbstick event and evt.detail.y and evt.detail.x\r\n// For Valve Index (maybe): touchstart, touchend, axismove?\r\n\r\nif (typeof AFRAME === 'undefined') {\r\n throw new Error('Component attempted to register before AFRAME was available.')\r\n}\r\n\r\nAFRAME.registerComponent('blink-controls', {\r\n schema: {\r\n // Button is a simplified startEvents & endEvents specification, e.g.\r\n // 'thumbstick' binds 'thumbstickdown' and 'thumbstickup' respectively\r\n button: { default: '', oneOf: ['trackpad', 'trigger', 'grip', 'menu', 'thumbstick'] },\r\n // The default teleport activation is a forward thumbstick axis,\r\n // but this can be changed with startEvents.\r\n startEvents: { type: 'array', default: [] },\r\n // The default teleport de-activation is a centered thumbstick axis,\r\n // but this can be changed with endEvents.\r\n endEvents: { type: 'array', default: [] },\r\n // Not assigned by default\r\n cancelEvents: { type: 'array', default: [] },\r\n collisionEntities: { default: '' },\r\n hitEntity: { type: 'selector' },\r\n cameraRig: { type: 'selector', default: '#player' },\r\n teleportOrigin: { type: 'selector', default: '#camera' },\r\n hitCylinderColor: { type: 'color', default: '#4d93fd' },\r\n hitCylinderRadius: { default: 0.25, min: 0 },\r\n hitCylinderHeight: { default: 0.3, min: 0 },\r\n interval: { default: 0 },\r\n curveNumberPoints: { default: 60, min: 2 },\r\n curveLineWidth: { default: 0.025 },\r\n curveHitColor: { type: 'color', default: '#4d93fd' },\r\n curveMissColor: { type: 'color', default: '#ff0000' },\r\n curveShootingSpeed: { default: 10, min: 0 },\r\n defaultPlaneSize: { default: 100 },\r\n landingNormal: { type: 'vec3', default: { x: 0, y: 1, z: 0 } },\r\n landingMaxAngle: { default: '45', min: 0, max: 360 },\r\n drawIncrementally: { default: true },\r\n incrementalDrawMs: { default: 300 },\r\n missOpacity: { default: 0.8 },\r\n hitOpacity: { default: 0.8 },\r\n snapTurn: { default: true },\r\n rotateOnTeleport: { default: true }\r\n },\r\n\r\n init: function () {\r\n const data = this.data\r\n const el = this.el\r\n let i\r\n\r\n this.active = false\r\n this.obj = el.object3D\r\n this.controllerPosition = new THREE.Vector3()\r\n this.hitEntityQuaternion = new THREE.Quaternion()\r\n // teleportOrigin is headset/camera with look-controls\r\n this.teleportOriginQuaternion = new THREE.Quaternion()\r\n this.hitPoint = new THREE.Vector3()\r\n this.collisionObjectNormalMatrix = new THREE.Matrix3()\r\n this.collisionWorldNormal = new THREE.Vector3()\r\n this.rigWorldPosition = new THREE.Vector3()\r\n this.newRigWorldPosition = new THREE.Vector3()\r\n this.teleportEventDetail = {\r\n oldPosition: this.rigWorldPosition,\r\n newPosition: this.newRigWorldPosition,\r\n hitPoint: this.hitPoint,\r\n rotationQuaternion: this.hitEntityQuaternion\r\n }\r\n\r\n this.hit = false\r\n this.prevCheckTime = undefined\r\n this.referenceNormal = new THREE.Vector3()\r\n this.curveMissColor = new THREE.Color()\r\n this.curveHitColor = new THREE.Color()\r\n this.raycaster = new THREE.Raycaster()\r\n\r\n this.defaultPlane = this.createDefaultPlane(this.data.defaultPlaneSize)\r\n this.defaultCollisionMeshes = [this.defaultPlane]\r\n\r\n const teleportEntity = this.teleportEntity = document.createElement('a-entity')\r\n teleportEntity.classList.add('teleportRay')\r\n teleportEntity.setAttribute('visible', false)\r\n el.sceneEl.appendChild(this.teleportEntity)\r\n\r\n this.onButtonDown = this.onButtonDown.bind(this)\r\n this.onButtonUp = this.onButtonUp.bind(this)\r\n this.cancel = this.cancel.bind(this)\r\n this.handleThumbstickAxis = this.handleThumbstickAxis.bind(this)\r\n\r\n this.teleportOrigin = this.data.teleportOrigin\r\n this.cameraRig = this.data.cameraRig\r\n\r\n this.snapturnRotation = THREE.MathUtils.degToRad(45)\r\n this.canSnapturn = true\r\n this.addedEvents = [];\r\n\r\n // Are startEvents and endEvents specified?\r\n if (this.data.startEvents.length && this.data.endEvents.length) {\r\n for (i = 0; i < this.data.startEvents.length; i++) {\r\n this.addedEvents.push([this.data.startEvents[i], this.onButtonDown])\r\n el.addEventListener(this.data.startEvents[i], this.onButtonDown)\r\n }\r\n for (i = 0; i < this.data.endEvents.length; i++) {\r\n this.addedEvents.push([this.data.endEvents[i], this.onButtonUp])\r\n el.addEventListener(this.data.endEvents[i], this.onButtonUp)\r\n }\r\n // Is a button for activation specified?\r\n } else if (data.button) {\r\n this.addedEvents.push([data.button + 'down', this.onButtonDown])\r\n this.addedEvents.push([data.button + 'up', this.onButtonUp])\r\n el.addEventListener(data.button + 'down', this.onButtonDown)\r\n el.addEventListener(data.button + 'up', this.onButtonUp)\r\n // If none of the above, default to thumbstick-axis based activation\r\n } else {\r\n this.thumbstickAxisActivation = true\r\n }\r\n \r\n for (i = 0; i < this.data.cancelEvents.length; i++) {\r\n this.addedEvents.push([this.data.cancelEvents[i], this.cancel])\r\n el.addEventListener(this.data.cancelEvents[i], this.cancel)\r\n }\r\n\r\n this.addedEvents.push(['thumbstickmoved', this.handleThumbstickAxis])\r\n el.addEventListener('thumbstickmoved', this.handleThumbstickAxis)\r\n this.queryCollisionEntities()\r\n },\r\n handleSnapturn: function (rotation, strength) {\r\n if (strength < 0.50) this.canSnapturn = true\r\n if (!this.canSnapturn) return\r\n // Only do snapturns if axis is very prominent (user intent is clear)\r\n // And preven further snapturns until axis returns to (close enough to) 0\r\n if (strength > 0.95) {\r\n if (Math.abs(rotation - Math.PI / 2.0) < 0.6) {\r\n this.cameraRig.object3D.rotateY(+this.snapturnRotation)\r\n this.canSnapturn = false\r\n } else if (Math.abs(rotation - 1.5 * Math.PI) < 0.6) {\r\n this.cameraRig.object3D.rotateY(-this.snapturnRotation)\r\n this.canSnapturn = false\r\n }\r\n }\r\n // if (rotation ) {\r\n // this.cameraRig.object3D.rotateY(-Math.sign(x) * this.snapturnRotation)\r\n // this.canSnapturn = false\r\n // }\r\n },\r\n handleThumbstickAxis: function (evt) {\r\n if (evt.detail.x !== undefined && evt.detail.y !== undefined) {\r\n const rotation = Math.atan2(evt.detail.x, evt.detail.y) + Math.PI\r\n const strength = Math.sqrt(evt.detail.x ** 2 + evt.detail.y ** 2)\r\n\r\n if (this.active) {\r\n // Only rotate if the axes are sufficiently prominent,\r\n // to prevent rotating in undesired/fluctuating directions.\r\n if (strength > 0.95) {\r\n this.obj.getWorldPosition(this.controllerPosition)\r\n this.controllerPosition.setComponent(1, this.hitEntity.object3D.position.y)\r\n // TODO: We set hitEntity invisible to prevent rotation glitches\r\n // but we could also rotate an invisible object instead and only\r\n // apply the final rotation to hitEntity.\r\n this.hitEntity.object3D.visible = false\r\n this.hitEntity.object3D.lookAt(this.controllerPosition)\r\n this.hitEntity.object3D.rotateY(rotation)\r\n this.hitEntity.object3D.visible = true\r\n this.hitEntity.object3D.getWorldQuaternion(this.hitEntityQuaternion)\r\n }\r\n if (Math.abs(evt.detail.x) === 0 && Math.abs(evt.detail.y) === 0) {\r\n // Disable teleport on axis return to 0 if axis (de)activation is enabled\r\n this.onButtonUp()\r\n }\r\n // Forward (rotation 0.0 || 6.28 is straight ahead)\r\n // We use half a radian left and right for some leeway\r\n // We also check for significant y axis movement to prevent\r\n // accidental teleports\r\n } else if (this.thumbstickAxisActivation && strength > 0.95 && (rotation < 0.50 || rotation > 5.78)) {\r\n // Activate (fuzzily) on forward axis if axis activation is enabled\r\n this.onButtonDown()\r\n } else if (this.data.snapTurn) {\r\n this.handleSnapturn(rotation, strength)\r\n }\r\n }\r\n },\r\n update: function (oldData) {\r\n const data = this.data\r\n const diff = AFRAME.utils.diff(data, oldData)\r\n\r\n // Update normal.\r\n this.referenceNormal.copy(data.landingNormal)\r\n\r\n // Update colors.\r\n this.curveMissColor.set(data.curveMissColor)\r\n this.curveHitColor.set(data.curveHitColor)\r\n\r\n // Create or update line mesh.\r\n if (!this.line ||\r\n 'curveLineWidth' in diff || 'curveNumberPoints' in diff || 'type' in diff) {\r\n this.line = this.createLine(data)\r\n this.line.material.opacity = this.data.hitOpacity\r\n this.line.material.transparent = this.data.hitOpacity < 1\r\n this.numActivePoints = data.curveNumberPoints\r\n this.teleportEntity.setObject3D('mesh', this.line.mesh)\r\n }\r\n\r\n // Create or update hit entity.\r\n if (data.hitEntity) {\r\n this.hitEntity = data.hitEntity\r\n } else if (!this.hitEntity || 'hitCylinderColor' in diff || 'hitCylinderHeight' in diff ||\r\n 'hitCylinderRadius' in diff) {\r\n // Remove previous entity, create new entity (could be more performant).\r\n if (this.hitEntity) { this.hitEntity.parentNode.removeChild(this.hitEntity) }\r\n this.hitEntity = this.createHitEntity(data)\r\n this.el.sceneEl.appendChild(this.hitEntity)\r\n }\r\n this.hitEntity.setAttribute('visible', false)\r\n\r\n // If it has rotation on teleport disabled hide the arrow indicating the teleportation direction \r\n if (!data.hitEntity) {\r\n this.hitEntity.lastElementChild.setAttribute('visible', data.rotateOnTeleport);\r\n }\r\n\r\n if ('collisionEntities' in diff) { this.queryCollisionEntities() }\r\n },\r\n\r\n remove: function () {\r\n const el = this.el\r\n const hitEntity = this.hitEntity\r\n const teleportEntity = this.teleportEntity\r\n\r\n if (hitEntity) { hitEntity.parentNode.removeChild(hitEntity) }\r\n if (teleportEntity) { teleportEntity.parentNode.removeChild(teleportEntity) }\r\n\r\n el.sceneEl.removeEventListener('child-attached', this.childAttachHandler)\r\n el.sceneEl.removeEventListener('child-detached', this.childDetachHandler)\r\n\r\n // Clean up event listeners if component removed but element isn't\r\n for (const [name, fn] of this.addedEvents) {\r\n el.removeEventListener(name, fn);\r\n }\r\n },\r\n\r\n tick: (function () {\r\n const p0 = new THREE.Vector3()\r\n const v0 = new THREE.Vector3()\r\n const g = -9.8\r\n const a = new THREE.Vector3(0, g, 0)\r\n const next = new THREE.Vector3()\r\n const last = new THREE.Vector3()\r\n const quaternion = new THREE.Quaternion()\r\n const translation = new THREE.Vector3()\r\n const scale = new THREE.Vector3()\r\n const shootAngle = new THREE.Vector3()\r\n const lastNext = new THREE.Vector3()\r\n const auxDirection = new THREE.Vector3()\r\n let timeSinceDrawStart = 0\r\n\r\n return function (time, delta) {\r\n if (!this.active) { return }\r\n if (this.data.drawIncrementally && this.redrawLine) {\r\n this.redrawLine = false\r\n timeSinceDrawStart = 0\r\n }\r\n timeSinceDrawStart += delta\r\n this.numActivePoints = this.data.curveNumberPoints * timeSinceDrawStart / this.data.incrementalDrawMs\r\n if (this.numActivePoints > this.data.curveNumberPoints) {\r\n this.numActivePoints = this.data.curveNumberPoints\r\n }\r\n\r\n // Only check for intersection if interval time has passed.\r\n if (this.prevCheckTime && (time - this.prevCheckTime < this.data.interval)) { return }\r\n // Update check time.\r\n this.prevCheckTime = time\r\n\r\n const matrixWorld = this.obj.matrixWorld\r\n matrixWorld.decompose(translation, quaternion, scale)\r\n\r\n const direction = shootAngle.set(0, 0, -1)\r\n .applyQuaternion(quaternion).normalize()\r\n this.line.setDirection(auxDirection.copy(direction))\r\n this.obj.getWorldPosition(p0)\r\n\r\n last.copy(p0)\r\n\r\n // Set default status as non-hit\r\n this.teleportEntity.setAttribute('visible', true)\r\n\r\n // But use hit color until ray animation finishes\r\n if (timeSinceDrawStart < this.data.incrementalDrawMs) {\r\n this.line.material.color.set(this.curveHitColor)\r\n } else {\r\n this.line.material.color.set(this.curveMissColor)\r\n }\r\n this.line.material.opacity = this.data.missOpacity\r\n this.line.material.transparent = this.data.missOpacity < 1\r\n this.hitEntity.setAttribute('visible', false)\r\n this.hit = false\r\n\r\n v0.copy(direction).multiplyScalar(this.data.curveShootingSpeed)\r\n\r\n this.lastDrawnIndex = 0\r\n const numPoints = this.data.drawIncrementally ? this.numActivePoints : this.line.numPoints\r\n for (let i = 0; i < numPoints + 1; i++) {\r\n let t\r\n if (i === Math.floor(numPoints + 1)) {\r\n t = numPoints / (this.line.numPoints - 1)\r\n } else {\r\n t = i / (this.line.numPoints - 1)\r\n }\r\n const timeToReach0 = this.parabolicCurveMaxRoot(p0, v0, a)\r\n t = t * Math.max(1, 1.5 * timeToReach0)\r\n\r\n this.parabolicCurve(p0, v0, a, t, next)\r\n // Update the raycaster with the length of the current segment last->next\r\n const dirLastNext = lastNext.copy(next).sub(last).normalize()\r\n this.raycaster.far = dirLastNext.length()\r\n this.raycaster.set(last, dirLastNext)\r\n\r\n this.lastDrawnPoint = next\r\n this.lastDrawnIndex = i\r\n if (this.checkMeshCollisions(i, last, next)) { break }\r\n\r\n last.copy(next)\r\n }\r\n for (let j = this.lastDrawnIndex + 1; j < this.line.numPoints; j++) {\r\n this.line.setPoint(j, this.lastDrawnPoint, this.lastDrawnPoint)\r\n }\r\n }\r\n })(),\r\n\r\n /**\r\n * Run `querySelectorAll` for `collisionEntities` and maintain it with `child-attached`\r\n * and `child-detached` events.\r\n */\r\n queryCollisionEntities: function () {\r\n const data = this.data\r\n const el = this.el\r\n\r\n if (!data.collisionEntities) {\r\n this.collisionEntities = []\r\n return\r\n }\r\n\r\n const collisionEntities = [].slice.call(el.sceneEl.querySelectorAll(data.collisionEntities))\r\n this.collisionEntities = collisionEntities\r\n\r\n // Update entity list on attach.\r\n this.childAttachHandler = function childAttachHandler (evt) {\r\n if (!evt.detail.el.matches(data.collisionEntities)) { return }\r\n collisionEntities.push(evt.detail.el)\r\n }\r\n el.sceneEl.addEventListener('child-attached', this.childAttachHandler)\r\n\r\n // Update entity list on detach.\r\n this.childDetachHandler = function childDetachHandler (evt) {\r\n if (!evt.detail.el.matches(data.collisionEntities)) { return }\r\n const index = collisionEntities.indexOf(evt.detail.el)\r\n if (index === -1) { return }\r\n collisionEntities.splice(index, 1)\r\n }\r\n el.sceneEl.addEventListener('child-detached', this.childDetachHandler)\r\n },\r\n\r\n onButtonDown: function () {\r\n this.active = true\r\n this.redrawLine = true\r\n },\r\n\r\n /**\r\n * Jump!\r\n */\r\n onButtonUp: (function () {\r\n const newRigLocalPosition = new THREE.Vector3()\r\n const newHandPosition = [new THREE.Vector3(), new THREE.Vector3()] // Left and right\r\n const handPosition = new THREE.Vector3()\r\n\r\n return function (evt) {\r\n if (!this.active) { return }\r\n\r\n // Hide the hit point and the curve\r\n this.active = false\r\n this.hitEntity.setAttribute('visible', false)\r\n this.teleportEntity.setAttribute('visible', false)\r\n\r\n if (!this.hit) {\r\n // Button released but no hit point\r\n return\r\n }\r\n\r\n const rig = this.data.cameraRig || this.el.sceneEl.camera.el\r\n rig.object3D.getWorldPosition(this.rigWorldPosition)\r\n this.newRigWorldPosition.copy(this.hitPoint)\r\n\r\n // Finally update the rigs position\r\n newRigLocalPosition.copy(this.newRigWorldPosition)\r\n if (rig.object3D.parent) {\r\n rig.object3D.parent.worldToLocal(newRigLocalPosition)\r\n }\r\n rig.setAttribute('position', newRigLocalPosition)\r\n\r\n // Also take the headset/camera rotation itself into account\r\n if (this.data.rotateOnTeleport) {\r\n this.teleportOriginQuaternion\r\n .setFromEuler(new THREE.Euler(0, this.teleportOrigin.object3D.rotation.y, 0))\r\n this.teleportOriginQuaternion.invert()\r\n this.teleportOriginQuaternion.multiply(this.hitEntityQuaternion)\r\n // Rotate the rig based on calculated teleport origin rotation\r\n this.cameraRig.object3D.setRotationFromQuaternion(this.teleportOriginQuaternion)\r\n }\r\n\r\n // If a rig was not explicitly declared, look for hands and move them proportionally as well\r\n if (!this.data.cameraRig) {\r\n const hands = document.querySelectorAll('a-entity[tracked-controls]')\r\n for (let i = 0; i < hands.length; i++) {\r\n hands[i].object3D.getWorldPosition(handPosition)\r\n\r\n // diff = rigWorldPosition - handPosition\r\n // newPos = newRigWorldPosition - diff\r\n newHandPosition[i].copy(this.newRigWorldPosition).sub(this.rigWorldPosition).add(handPosition)\r\n hands[i].setAttribute('position', newHandPosition[i])\r\n }\r\n }\r\n\r\n this.el.emit('teleported', this.teleportEventDetail)\r\n }\r\n })(),\r\n\r\n cancel: function () {\r\n this.active = false\r\n this.hitEntity.setAttribute('visible', false)\r\n this.teleportEntity.setAttribute('visible', false)\r\n },\r\n\r\n /**\r\n * Check for raycaster intersection.\r\n *\r\n * @param {number} Line fragment point index.\r\n * @param {number} Last line fragment point index.\r\n * @param {number} Next line fragment point index.\r\n * @returns {boolean} true if there's an intersection.\r\n */\r\n checkMeshCollisions: function (i, last, next) {\r\n // @todo We should add a property to define if the collisionEntity is dynamic or static\r\n // If static we should do the map just once, otherwise we're recreating the array in every\r\n // loop when aiming.\r\n let meshes\r\n if (!this.data.collisionEntities) {\r\n meshes = this.defaultCollisionMeshes\r\n } else {\r\n meshes = this.collisionEntities.map(function (entity) {\r\n return entity.getObject3D('mesh')\r\n }).filter(function (n) { return n })\r\n meshes = meshes.length ? meshes : this.defaultCollisionMeshes\r\n }\r\n\r\n const intersects = this.raycaster.intersectObjects(meshes, true)\r\n if (intersects.length > 0 && !this.hit &&\r\n this.isValidNormalsAngle(intersects[0].face.normal, intersects[0].object)) {\r\n const point = intersects[0].point\r\n\r\n this.line.material.color.set(this.curveHitColor)\r\n this.line.material.opacity = this.data.hitOpacity\r\n this.line.material.transparent = this.data.hitOpacity < 1\r\n this.hitEntity.setAttribute('position', point)\r\n this.hitEntity.setAttribute('visible', true)\r\n\r\n this.hit = true\r\n this.hitPoint.copy(intersects[0].point)\r\n\r\n // If hit, just fill the rest of the points with the hit point and break the loop\r\n for (let j = i; j < this.line.numPoints; j++) {\r\n this.line.setPoint(j, last, this.hitPoint)\r\n }\r\n return true\r\n } else {\r\n this.line.setPoint(i, last, next)\r\n return false\r\n }\r\n },\r\n\r\n isValidNormalsAngle: function (collisionNormal, collisionObject) {\r\n this.collisionObjectNormalMatrix.getNormalMatrix(collisionObject.matrixWorld)\r\n this.collisionWorldNormal.copy(collisionNormal)\r\n .applyMatrix3(this.collisionObjectNormalMatrix).normalize()\r\n const angleNormals = this.referenceNormal.angleTo(this.collisionWorldNormal)\r\n return (THREE.MathUtils.RAD2DEG * angleNormals <= this.data.landingMaxAngle)\r\n },\r\n\r\n // Utils\r\n // Parabolic motion equation, y = p0 + v0*t + 1/2at^2\r\n parabolicCurveScalar: function (p0, v0, a, t) {\r\n return p0 + v0 * t + 0.5 * a * t * t\r\n },\r\n\r\n // Parabolic motion equation applied to 3 dimensions\r\n parabolicCurve: function (p0, v0, a, t, out) {\r\n out.x = this.parabolicCurveScalar(p0.x, v0.x, a.x, t)\r\n out.y = this.parabolicCurveScalar(p0.y, v0.y, a.y, t)\r\n out.z = this.parabolicCurveScalar(p0.z, v0.z, a.z, t)\r\n return out\r\n },\r\n\r\n // To determine how long in terms of t we need to calculate\r\n parabolicCurveMaxRoot: function (p0, v0, a) {\r\n const root = (-v0.y - Math.sqrt(v0.y ** 2 - 4 * (0.5 * a.y) * p0.y)) / (2 * 0.5 * a.y)\r\n return root\r\n },\r\n\r\n createLine: function (data) {\r\n const numPoints = data.type === 'line' ? 2 : data.curveNumberPoints\r\n return new AFRAME.utils.RayCurve(numPoints, data.curveLineWidth)\r\n },\r\n\r\n /**\r\n * Create mesh to represent the area of intersection.\r\n * Default to a combination of torus and cylinder.\r\n */\r\n createHitEntity: function (data) {\r\n // Parent.\r\n const hitEntity = document.createElement('a-entity')\r\n hitEntity.className = 'hitEntity'\r\n\r\n // Torus.\r\n const torus = document.createElement('a-entity')\r\n torus.setAttribute('geometry', {\r\n primitive: 'torus',\r\n radius: data.hitCylinderRadius,\r\n radiusTubular: 0.01\r\n })\r\n torus.setAttribute('rotation', { x: 90, y: 0, z: 0 })\r\n torus.setAttribute('material', {\r\n shader: 'flat',\r\n color: data.hitCylinderColor,\r\n side: 'double',\r\n depthTest: false\r\n })\r\n hitEntity.appendChild(torus)\r\n\r\n // Cylinder.\r\n const cylinder = document.createElement('a-entity')\r\n cylinder.setAttribute('position', { x: 0, y: data.hitCylinderHeight / 2, z: 0 })\r\n cylinder.setAttribute('geometry', {\r\n primitive: 'cylinder',\r\n segmentsHeight: 1,\r\n radius: data.hitCylinderRadius,\r\n height: data.hitCylinderHeight,\r\n openEnded: true\r\n })\r\n cylinder.setAttribute('material', {\r\n shader: 'flat',\r\n color: data.hitCylinderColor,\r\n opacity: 0.5,\r\n side: 'double',\r\n src: this.cylinderTexture,\r\n transparent: true,\r\n depthTest: false\r\n })\r\n hitEntity.appendChild(cylinder)\r\n\r\n const pointer = document.createElement('a-entity')\r\n pointer.setAttribute('position', { x: 0, y: 0.05, z: data.hitCylinderRadius * -1.5 })\r\n pointer.setAttribute('rotation', { x: 90, y: 180, z: 0 })\r\n pointer.setAttribute('geometry', {\r\n primitive: 'prism',\r\n height: 0.2,\r\n width: 0.2,\r\n depth: 0.05\r\n })\r\n pointer.setAttribute('material', {\r\n shader: 'flat',\r\n color: data.hitCylinderColor,\r\n side: 'double',\r\n transparent: true,\r\n opacity: 0.6,\r\n depthTest: false\r\n })\r\n hitEntity.appendChild(pointer)\r\n\r\n return hitEntity\r\n },\r\n createDefaultPlane: function (size) {\r\n const geometry = new THREE.PlaneGeometry(100, 100)\r\n geometry.rotateX(-Math.PI / 2)\r\n const material = new THREE.MeshBasicMaterial({ color: 0xffff00 })\r\n return new THREE.Mesh(geometry, material)\r\n },\r\n cylinderTexture: 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAQCAYAAADXnxW3AAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAADJJREFUeNpEx7ENgDAAAzArK0JA6f8X9oewlcWStU1wBGdwB08wgjeYm79jc2nbYH0DAC/+CORJxO5fAAAAAElFTkSuQmCC)'\r\n})\r\n\r\nAFRAME.utils.RayCurve = function (numPoints, width) {\r\n this.geometry = new THREE.BufferGeometry()\r\n this.vertices = new Float32Array(numPoints * 3 * 6) // 6 vertices (2 triangles) * 3 dimensions\r\n this.uvs = new Float32Array(numPoints * 2 * 6) // 2 uvs per vertex\r\n this.width = width\r\n\r\n this.geometry.setAttribute('position', new THREE.BufferAttribute(this.vertices, 3).setUsage(THREE.DynamicDrawUsage))\r\n\r\n this.material = new THREE.MeshBasicMaterial({\r\n side: THREE.DoubleSide,\r\n color: 0xff0000\r\n })\r\n\r\n this.mesh = new THREE.Mesh(this.geometry, this.material)\r\n\r\n this.mesh.frustumCulled = false\r\n this.mesh.vertices = this.vertices\r\n\r\n this.direction = new THREE.Vector3()\r\n this.numPoints = numPoints\r\n}\r\n\r\nAFRAME.utils.RayCurve.prototype = {\r\n setDirection: function (direction) {\r\n const UP = new THREE.Vector3(0, 1, 0)\r\n this.direction\r\n .copy(direction)\r\n .cross(UP)\r\n .normalize()\r\n .multiplyScalar(this.width / 2)\r\n },\r\n\r\n setWidth: function (width) {\r\n this.width = width\r\n },\r\n\r\n setPoint: (function () {\r\n const posA = new THREE.Vector3()\r\n const posB = new THREE.Vector3()\r\n const posC = new THREE.Vector3()\r\n const posD = new THREE.Vector3()\r\n\r\n return function (i, last, next) {\r\n posA.copy(last).add(this.direction)\r\n posB.copy(last).sub(this.direction)\r\n\r\n posC.copy(next).add(this.direction)\r\n posD.copy(next).sub(this.direction)\r\n\r\n let idx = 6 * 3 * i // 6 vertices per point\r\n\r\n this.vertices[idx++] = posA.x\r\n this.vertices[idx++] = posA.y\r\n this.vertices[idx++] = posA.z\r\n\r\n this.vertices[idx++] = posB.x\r\n this.vertices[idx++] = posB.y\r\n this.vertices[idx++] = posB.z\r\n\r\n this.vertices[idx++] = posC.x\r\n this.vertices[idx++] = posC.y\r\n this.vertices[idx++] = posC.z\r\n\r\n this.vertices[idx++] = posC.x\r\n this.vertices[idx++] = posC.y\r\n this.vertices[idx++] = posC.z\r\n\r\n this.vertices[idx++] = posB.x\r\n this.vertices[idx++] = posB.y\r\n this.vertices[idx++] = posB.z\r\n\r\n this.vertices[idx++] = posD.x\r\n this.vertices[idx++] = posD.y\r\n this.vertices[idx++] = posD.z\r\n\r\n this.geometry.attributes.position.needsUpdate = true\r\n }\r\n })()\r\n}\r\n"],"names":[],"sourceRoot":""} \ No newline at end of file diff --git a/dist/aframe-blink-controls.min.js b/dist/aframe-blink-controls.min.js index a79e888..8d2f514 100644 --- a/dist/aframe-blink-controls.min.js +++ b/dist/aframe-blink-controls.min.js @@ -1,2 +1,2 @@ -(()=>{if(AFRAME.registerGeometry("prism",{schema:{depth:{default:1,min:0},height:{default:1,min:0},width:{default:1,min:0}},init:function(t){const i=new THREE.Shape;i.moveTo(t.width/2,0),i.lineTo(0,t.height),i.lineTo(-t.width/2,0),i.lineTo(t.width/2,0);const e={steps:2,depth:t.depth,bevelEnabled:!1};this.geometry=new THREE.ExtrudeGeometry(i,e)}}),"undefined"==typeof AFRAME)throw new Error("Component attempted to register before AFRAME was available.");AFRAME.registerComponent("blink-controls",{schema:{button:{default:"",oneOf:["trackpad","trigger","grip","menu","thumbstick"]},startEvents:{type:"array",default:[]},endEvents:{type:"array",default:[]},cancelEvents:{type:"array",default:[]},collisionEntities:{default:""},hitEntity:{type:"selector"},cameraRig:{type:"selector",default:"#player"},teleportOrigin:{type:"selector",default:"#camera"},hitCylinderColor:{type:"color",default:"#4d93fd"},hitCylinderRadius:{default:.25,min:0},hitCylinderHeight:{default:.3,min:0},interval:{default:0},curveNumberPoints:{default:60,min:2},curveLineWidth:{default:.025},curveHitColor:{type:"color",default:"#4d93fd"},curveMissColor:{type:"color",default:"#ff0000"},curveShootingSpeed:{default:10,min:0},defaultPlaneSize:{default:100},landingNormal:{type:"vec3",default:{x:0,y:1,z:0}},landingMaxAngle:{default:"45",min:0,max:360},drawIncrementally:{default:!0},incrementalDrawMs:{default:300},missOpacity:{default:.8},hitOpacity:{default:.8},snapTurn:{default:!0},rotateOnTeleport:{default:!0}},init:function(){const t=this.data,i=this.el;let e;this.active=!1,this.obj=i.object3D,this.controllerPosition=new THREE.Vector3,this.hitEntityQuaternion=new THREE.Quaternion,this.teleportOriginQuaternion=new THREE.Quaternion,this.hitPoint=new THREE.Vector3,this.collisionObjectNormalMatrix=new THREE.Matrix3,this.collisionWorldNormal=new THREE.Vector3,this.rigWorldPosition=new THREE.Vector3,this.newRigWorldPosition=new THREE.Vector3,this.teleportEventDetail={oldPosition:this.rigWorldPosition,newPosition:this.newRigWorldPosition,hitPoint:this.hitPoint,rotationQuaternion:this.hitEntityQuaternion},this.hit=!1,this.prevCheckTime=void 0,this.referenceNormal=new THREE.Vector3,this.curveMissColor=new THREE.Color,this.curveHitColor=new THREE.Color,this.raycaster=new THREE.Raycaster,this.defaultPlane=this.createDefaultPlane(this.data.defaultPlaneSize),this.defaultCollisionMeshes=[this.defaultPlane];const n=this.teleportEntity=document.createElement("a-entity");if(n.classList.add("teleportRay"),n.setAttribute("visible",!1),i.sceneEl.appendChild(this.teleportEntity),this.onButtonDown=this.onButtonDown.bind(this),this.onButtonUp=this.onButtonUp.bind(this),this.cancel=this.cancel.bind(this),this.handleThumbstickAxis=this.handleThumbstickAxis.bind(this),this.teleportOrigin=this.data.teleportOrigin,this.cameraRig=this.data.cameraRig,this.snapturnRotation=THREE.MathUtils.degToRad(45),this.canSnapturn=!0,this.addedEvents=[],this.data.startEvents.length&&this.data.endEvents.length){for(e=0;e.95&&(Math.abs(t-Math.PI/2)<.6?(this.cameraRig.object3D.rotateY(+this.snapturnRotation),this.canSnapturn=!1):Math.abs(t-1.5*Math.PI)<.6&&(this.cameraRig.object3D.rotateY(-this.snapturnRotation),this.canSnapturn=!1))},handleThumbstickAxis:function(t){if(void 0!==t.detail.x&&void 0!==t.detail.y){const i=Math.atan2(t.detail.x,t.detail.y)+Math.PI,e=Math.sqrt(t.detail.x**2+t.detail.y**2);this.active?(e>.95&&(this.obj.getWorldPosition(this.controllerPosition),this.controllerPosition.setComponent(1,this.hitEntity.object3D.position.y),this.hitEntity.object3D.visible=!1,this.hitEntity.object3D.lookAt(this.controllerPosition),this.hitEntity.object3D.rotateY(i),this.hitEntity.object3D.visible=!0,this.hitEntity.object3D.getWorldQuaternion(this.hitEntityQuaternion)),0===Math.abs(t.detail.x)&&0===Math.abs(t.detail.y)&&this.onButtonUp()):this.thumbstickAxisActivation&&e>.95&&(i<.5||i>5.78)?this.onButtonDown():this.data.snapTurn&&this.handleSnapturn(i,e)}},update:function(t){const i=this.data,e=AFRAME.utils.diff(i,t);this.referenceNormal.copy(i.landingNormal),this.curveMissColor.set(i.curveMissColor),this.curveHitColor.set(i.curveHitColor),(!this.line||"curveLineWidth"in e||"curveNumberPoints"in e||"type"in e)&&(this.line=this.createLine(i),this.line.material.opacity=this.data.hitOpacity,this.line.material.transparent=this.data.hitOpacity<1,this.numActivePoints=i.curveNumberPoints,this.teleportEntity.setObject3D("mesh",this.line.mesh)),i.hitEntity?this.hitEntity=i.hitEntity:(!this.hitEntity||"hitCylinderColor"in e||"hitCylinderHeight"in e||"hitCylinderRadius"in e)&&(this.hitEntity&&this.hitEntity.parentNode.removeChild(this.hitEntity),this.hitEntity=this.createHitEntity(i),this.el.sceneEl.appendChild(this.hitEntity)),this.hitEntity.setAttribute("visible",!1),i.hitEntity||this.hitEntity.lastElementChild.setAttribute("visible",i.rotateOnTeleport),"collisionEntities"in e&&this.queryCollisionEntities()},remove:function(){const t=this.el,i=this.hitEntity,e=this.teleportEntity;i&&i.parentNode.removeChild(i),e&&e.parentNode.removeChild(e),t.sceneEl.removeEventListener("child-attached",this.childAttachHandler),t.sceneEl.removeEventListener("child-detached",this.childDetachHandler);for(const[i,e]of this.addedEvents)t.removeEventListener(i,e)},tick:function(){const t=new THREE.Vector3,i=new THREE.Vector3,e=new THREE.Vector3(0,-9.8,0),n=new THREE.Vector3,s=new THREE.Vector3,o=new THREE.Quaternion,r=new THREE.Vector3,a=new THREE.Vector3,h=new THREE.Vector3,l=new THREE.Vector3,c=new THREE.Vector3;let d=0;return function(u,E){if(!this.active)return;if(this.data.drawIncrementally&&this.redrawLine&&(this.redrawLine=!1,d=0),d+=E,this.numActivePoints=this.data.curveNumberPoints*d/this.data.incrementalDrawMs,this.numActivePoints>this.data.curveNumberPoints&&(this.numActivePoints=this.data.curveNumberPoints),this.prevCheckTime&&u-this.prevCheckTime0&&!this.hit&&this.isValidNormalsAngle(s[0].face.normal,s[0].object)){const e=s[0].point;this.line.material.color.set(this.curveHitColor),this.line.material.opacity=this.data.hitOpacity,this.line.material.transparent=this.data.hitOpacity<1,this.hitEntity.setAttribute("position",e),this.hitEntity.setAttribute("visible",!0),this.hit=!0,this.hitPoint.copy(s[0].point);for(let e=t;e{if(AFRAME.registerGeometry("prism",{schema:{depth:{default:1,min:0},height:{default:1,min:0},width:{default:1,min:0}},init:function(t){const i=new THREE.Shape;i.moveTo(t.width/2,0),i.lineTo(0,t.height),i.lineTo(-t.width/2,0),i.lineTo(t.width/2,0);const e={steps:2,depth:t.depth,bevelEnabled:!1};this.geometry=new THREE.ExtrudeGeometry(i,e)}}),"undefined"==typeof AFRAME)throw new Error("Component attempted to register before AFRAME was available.");AFRAME.registerComponent("blink-controls",{schema:{button:{default:"",oneOf:["trackpad","trigger","grip","menu","thumbstick"]},startEvents:{type:"array",default:[]},endEvents:{type:"array",default:[]},cancelEvents:{type:"array",default:[]},collisionEntities:{default:""},hitEntity:{type:"selector"},cameraRig:{type:"selector",default:"#player"},teleportOrigin:{type:"selector",default:"#camera"},hitCylinderColor:{type:"color",default:"#4d93fd"},hitCylinderRadius:{default:.25,min:0},hitCylinderHeight:{default:.3,min:0},interval:{default:0},curveNumberPoints:{default:60,min:2},curveLineWidth:{default:.025},curveHitColor:{type:"color",default:"#4d93fd"},curveMissColor:{type:"color",default:"#ff0000"},curveShootingSpeed:{default:10,min:0},defaultPlaneSize:{default:100},landingNormal:{type:"vec3",default:{x:0,y:1,z:0}},landingMaxAngle:{default:"45",min:0,max:360},drawIncrementally:{default:!0},incrementalDrawMs:{default:300},missOpacity:{default:.8},hitOpacity:{default:.8},snapTurn:{default:!0},rotateOnTeleport:{default:!0}},init:function(){const t=this.data,i=this.el;let e;this.active=!1,this.obj=i.object3D,this.controllerPosition=new THREE.Vector3,this.hitEntityQuaternion=new THREE.Quaternion,this.teleportOriginQuaternion=new THREE.Quaternion,this.hitPoint=new THREE.Vector3,this.collisionObjectNormalMatrix=new THREE.Matrix3,this.collisionWorldNormal=new THREE.Vector3,this.rigWorldPosition=new THREE.Vector3,this.newRigWorldPosition=new THREE.Vector3,this.teleportEventDetail={oldPosition:this.rigWorldPosition,newPosition:this.newRigWorldPosition,hitPoint:this.hitPoint,rotationQuaternion:this.hitEntityQuaternion},this.hit=!1,this.prevCheckTime=void 0,this.referenceNormal=new THREE.Vector3,this.curveMissColor=new THREE.Color,this.curveHitColor=new THREE.Color,this.raycaster=new THREE.Raycaster,this.defaultPlane=this.createDefaultPlane(this.data.defaultPlaneSize),this.defaultCollisionMeshes=[this.defaultPlane];const n=this.teleportEntity=document.createElement("a-entity");if(n.classList.add("teleportRay"),n.setAttribute("visible",!1),i.sceneEl.appendChild(this.teleportEntity),this.onButtonDown=this.onButtonDown.bind(this),this.onButtonUp=this.onButtonUp.bind(this),this.cancel=this.cancel.bind(this),this.handleThumbstickAxis=this.handleThumbstickAxis.bind(this),this.teleportOrigin=this.data.teleportOrigin,this.cameraRig=this.data.cameraRig,this.snapturnRotation=THREE.MathUtils.degToRad(45),this.canSnapturn=!0,this.addedEvents=[],this.data.startEvents.length&&this.data.endEvents.length){for(e=0;e.95&&(Math.abs(t-Math.PI/2)<.6?(this.cameraRig.object3D.rotateY(+this.snapturnRotation),this.canSnapturn=!1):Math.abs(t-1.5*Math.PI)<.6&&(this.cameraRig.object3D.rotateY(-this.snapturnRotation),this.canSnapturn=!1))},handleThumbstickAxis:function(t){if(void 0!==t.detail.x&&void 0!==t.detail.y){const i=Math.atan2(t.detail.x,t.detail.y)+Math.PI,e=Math.sqrt(t.detail.x**2+t.detail.y**2);this.active?(e>.95&&(this.obj.getWorldPosition(this.controllerPosition),this.controllerPosition.setComponent(1,this.hitEntity.object3D.position.y),this.hitEntity.object3D.visible=!1,this.hitEntity.object3D.lookAt(this.controllerPosition),this.hitEntity.object3D.rotateY(i),this.hitEntity.object3D.visible=!0,this.hitEntity.object3D.getWorldQuaternion(this.hitEntityQuaternion)),0===Math.abs(t.detail.x)&&0===Math.abs(t.detail.y)&&this.onButtonUp()):this.thumbstickAxisActivation&&e>.95&&(i<.5||i>5.78)?this.onButtonDown():this.data.snapTurn&&this.handleSnapturn(i,e)}},update:function(t){const i=this.data,e=AFRAME.utils.diff(i,t);this.referenceNormal.copy(i.landingNormal),this.curveMissColor.set(i.curveMissColor),this.curveHitColor.set(i.curveHitColor),(!this.line||"curveLineWidth"in e||"curveNumberPoints"in e||"type"in e)&&(this.line=this.createLine(i),this.line.material.opacity=this.data.hitOpacity,this.line.material.transparent=this.data.hitOpacity<1,this.numActivePoints=i.curveNumberPoints,this.teleportEntity.setObject3D("mesh",this.line.mesh)),i.hitEntity?this.hitEntity=i.hitEntity:(!this.hitEntity||"hitCylinderColor"in e||"hitCylinderHeight"in e||"hitCylinderRadius"in e)&&(this.hitEntity&&this.hitEntity.parentNode.removeChild(this.hitEntity),this.hitEntity=this.createHitEntity(i),this.el.sceneEl.appendChild(this.hitEntity)),this.hitEntity.setAttribute("visible",!1),i.hitEntity||this.hitEntity.lastElementChild.setAttribute("visible",i.rotateOnTeleport),"collisionEntities"in e&&this.queryCollisionEntities()},remove:function(){const t=this.el,i=this.hitEntity,e=this.teleportEntity;i&&i.parentNode.removeChild(i),e&&e.parentNode.removeChild(e),t.sceneEl.removeEventListener("child-attached",this.childAttachHandler),t.sceneEl.removeEventListener("child-detached",this.childDetachHandler);for(const[i,e]of this.addedEvents)t.removeEventListener(i,e)},tick:function(){const t=new THREE.Vector3,i=new THREE.Vector3,e=new THREE.Vector3(0,-9.8,0),n=new THREE.Vector3,s=new THREE.Vector3,o=new THREE.Quaternion,r=new THREE.Vector3,a=new THREE.Vector3,h=new THREE.Vector3,l=new THREE.Vector3,c=new THREE.Vector3;let d=0;return function(u,E){if(!this.active)return;if(this.data.drawIncrementally&&this.redrawLine&&(this.redrawLine=!1,d=0),d+=E,this.numActivePoints=this.data.curveNumberPoints*d/this.data.incrementalDrawMs,this.numActivePoints>this.data.curveNumberPoints&&(this.numActivePoints=this.data.curveNumberPoints),this.prevCheckTime&&u-this.prevCheckTime0&&!this.hit&&this.isValidNormalsAngle(s[0].face.normal,s[0].object)){const e=s[0].point;this.line.material.color.set(this.curveHitColor),this.line.material.opacity=this.data.hitOpacity,this.line.material.transparent=this.data.hitOpacity<1,this.hitEntity.setAttribute("position",e),this.hitEntity.setAttribute("visible",!0),this.hit=!0,this.hitPoint.copy(s[0].point);for(let e=t;e 0.95) {\r\n if (Math.abs(rotation - Math.PI / 2.0) < 0.6) {\r\n this.cameraRig.object3D.rotateY(+this.snapturnRotation)\r\n this.canSnapturn = false\r\n } else if (Math.abs(rotation - 1.5 * Math.PI) < 0.6) {\r\n this.cameraRig.object3D.rotateY(-this.snapturnRotation)\r\n this.canSnapturn = false\r\n }\r\n }\r\n // if (rotation ) {\r\n // this.cameraRig.object3D.rotateY(-Math.sign(x) * this.snapturnRotation)\r\n // this.canSnapturn = false\r\n // }\r\n },\r\n handleThumbstickAxis: function (evt) {\r\n if (evt.detail.x !== undefined && evt.detail.y !== undefined) {\r\n const rotation = Math.atan2(evt.detail.x, evt.detail.y) + Math.PI\r\n const strength = Math.sqrt(evt.detail.x ** 2 + evt.detail.y ** 2)\r\n\r\n if (this.active) {\r\n // Only rotate if the axes are sufficiently prominent,\r\n // to prevent rotating in undesired/fluctuating directions.\r\n if (strength > 0.95) {\r\n this.obj.getWorldPosition(this.controllerPosition)\r\n this.controllerPosition.setComponent(1, this.hitEntity.object3D.position.y)\r\n // TODO: We set hitEntity invisible to prevent rotation glitches\r\n // but we could also rotate an invisible object instead and only\r\n // apply the final rotation to hitEntity.\r\n this.hitEntity.object3D.visible = false\r\n this.hitEntity.object3D.lookAt(this.controllerPosition)\r\n this.hitEntity.object3D.rotateY(rotation)\r\n this.hitEntity.object3D.visible = true\r\n this.hitEntity.object3D.getWorldQuaternion(this.hitEntityQuaternion)\r\n }\r\n if (Math.abs(evt.detail.x) === 0 && Math.abs(evt.detail.y) === 0) {\r\n // Disable teleport on axis return to 0 if axis (de)activation is enabled\r\n this.onButtonUp()\r\n }\r\n // Forward (rotation 0.0 || 6.28 is straight ahead)\r\n // We use half a radian left and right for some leeway\r\n // We also check for significant y axis movement to prevent\r\n // accidental teleports\r\n } else if (this.thumbstickAxisActivation && strength > 0.95 && (rotation < 0.50 || rotation > 5.78)) {\r\n // Activate (fuzzily) on forward axis if axis activation is enabled\r\n this.onButtonDown()\r\n } else if (this.data.snapTurn) {\r\n this.handleSnapturn(rotation, strength)\r\n }\r\n }\r\n },\r\n update: function (oldData) {\r\n const data = this.data\r\n const diff = AFRAME.utils.diff(data, oldData)\r\n\r\n // Update normal.\r\n this.referenceNormal.copy(data.landingNormal)\r\n\r\n // Update colors.\r\n this.curveMissColor.set(data.curveMissColor)\r\n this.curveHitColor.set(data.curveHitColor)\r\n\r\n // Create or update line mesh.\r\n if (!this.line ||\r\n 'curveLineWidth' in diff || 'curveNumberPoints' in diff || 'type' in diff) {\r\n this.line = this.createLine(data)\r\n this.line.material.opacity = this.data.hitOpacity\r\n this.line.material.transparent = this.data.hitOpacity < 1\r\n this.numActivePoints = data.curveNumberPoints\r\n this.teleportEntity.setObject3D('mesh', this.line.mesh)\r\n }\r\n\r\n // Create or update hit entity.\r\n if (data.hitEntity) {\r\n this.hitEntity = data.hitEntity\r\n } else if (!this.hitEntity || 'hitCylinderColor' in diff || 'hitCylinderHeight' in diff ||\r\n 'hitCylinderRadius' in diff) {\r\n // Remove previous entity, create new entity (could be more performant).\r\n if (this.hitEntity) { this.hitEntity.parentNode.removeChild(this.hitEntity) }\r\n this.hitEntity = this.createHitEntity(data)\r\n this.el.sceneEl.appendChild(this.hitEntity)\r\n }\r\n this.hitEntity.setAttribute('visible', false)\r\n\r\n // If it has rotation on teleport disabled hide the arrow indicating the teleportation direction \r\n if (!data.hitEntity) {\r\n this.hitEntity.lastElementChild.setAttribute('visible', data.rotateOnTeleport);\r\n }\r\n\r\n if ('collisionEntities' in diff) { this.queryCollisionEntities() }\r\n },\r\n\r\n remove: function () {\r\n const el = this.el\r\n const hitEntity = this.hitEntity\r\n const teleportEntity = this.teleportEntity\r\n\r\n if (hitEntity) { hitEntity.parentNode.removeChild(hitEntity) }\r\n if (teleportEntity) { teleportEntity.parentNode.removeChild(teleportEntity) }\r\n\r\n el.sceneEl.removeEventListener('child-attached', this.childAttachHandler)\r\n el.sceneEl.removeEventListener('child-detached', this.childDetachHandler)\r\n\r\n // Clean up event listeners if component removed but element isn't\r\n for (const [name, fn] of this.addedEvents) {\r\n el.removeEventListener(name, fn);\r\n }\r\n },\r\n\r\n tick: (function () {\r\n const p0 = new THREE.Vector3()\r\n const v0 = new THREE.Vector3()\r\n const g = -9.8\r\n const a = new THREE.Vector3(0, g, 0)\r\n const next = new THREE.Vector3()\r\n const last = new THREE.Vector3()\r\n const quaternion = new THREE.Quaternion()\r\n const translation = new THREE.Vector3()\r\n const scale = new THREE.Vector3()\r\n const shootAngle = new THREE.Vector3()\r\n const lastNext = new THREE.Vector3()\r\n const auxDirection = new THREE.Vector3()\r\n let timeSinceDrawStart = 0\r\n\r\n return function (time, delta) {\r\n if (!this.active) { return }\r\n if (this.data.drawIncrementally && this.redrawLine) {\r\n this.redrawLine = false\r\n timeSinceDrawStart = 0\r\n }\r\n timeSinceDrawStart += delta\r\n this.numActivePoints = this.data.curveNumberPoints * timeSinceDrawStart / this.data.incrementalDrawMs\r\n if (this.numActivePoints > this.data.curveNumberPoints) {\r\n this.numActivePoints = this.data.curveNumberPoints\r\n }\r\n\r\n // Only check for intersection if interval time has passed.\r\n if (this.prevCheckTime && (time - this.prevCheckTime < this.data.interval)) { return }\r\n // Update check time.\r\n this.prevCheckTime = time\r\n\r\n const matrixWorld = this.obj.matrixWorld\r\n matrixWorld.decompose(translation, quaternion, scale)\r\n\r\n const direction = shootAngle.set(0, 0, -1)\r\n .applyQuaternion(quaternion).normalize()\r\n this.line.setDirection(auxDirection.copy(direction))\r\n this.obj.getWorldPosition(p0)\r\n\r\n last.copy(p0)\r\n\r\n // Set default status as non-hit\r\n this.teleportEntity.setAttribute('visible', true)\r\n\r\n // But use hit color until ray animation finishes\r\n if (timeSinceDrawStart < this.data.incrementalDrawMs) {\r\n this.line.material.color.set(this.curveHitColor)\r\n } else {\r\n this.line.material.color.set(this.curveMissColor)\r\n }\r\n this.line.material.opacity = this.data.missOpacity\r\n this.line.material.transparent = this.data.missOpacity < 1\r\n this.hitEntity.setAttribute('visible', false)\r\n this.hit = false\r\n\r\n v0.copy(direction).multiplyScalar(this.data.curveShootingSpeed)\r\n\r\n this.lastDrawnIndex = 0\r\n const numPoints = this.data.drawIncrementally ? this.numActivePoints : this.line.numPoints\r\n for (let i = 0; i < numPoints + 1; i++) {\r\n let t\r\n if (i === Math.floor(numPoints + 1)) {\r\n t = numPoints / (this.line.numPoints - 1)\r\n } else {\r\n t = i / (this.line.numPoints - 1)\r\n }\r\n const timeToReach0 = this.parabolicCurveMaxRoot(p0, v0, a)\r\n t = t * Math.max(1, 1.5 * timeToReach0)\r\n\r\n this.parabolicCurve(p0, v0, a, t, next)\r\n // Update the raycaster with the length of the current segment last->next\r\n const dirLastNext = lastNext.copy(next).sub(last).normalize()\r\n this.raycaster.far = dirLastNext.length()\r\n this.raycaster.set(last, dirLastNext)\r\n\r\n this.lastDrawnPoint = next\r\n this.lastDrawnIndex = i\r\n if (this.checkMeshCollisions(i, last, next)) { break }\r\n\r\n last.copy(next)\r\n }\r\n for (let j = this.lastDrawnIndex + 1; j < this.line.numPoints; j++) {\r\n this.line.setPoint(j, this.lastDrawnPoint, this.lastDrawnPoint)\r\n }\r\n }\r\n })(),\r\n\r\n /**\r\n * Run `querySelectorAll` for `collisionEntities` and maintain it with `child-attached`\r\n * and `child-detached` events.\r\n */\r\n queryCollisionEntities: function () {\r\n const data = this.data\r\n const el = this.el\r\n\r\n if (!data.collisionEntities) {\r\n this.collisionEntities = []\r\n return\r\n }\r\n\r\n const collisionEntities = [].slice.call(el.sceneEl.querySelectorAll(data.collisionEntities))\r\n this.collisionEntities = collisionEntities\r\n\r\n // Update entity list on attach.\r\n this.childAttachHandler = function childAttachHandler (evt) {\r\n if (!evt.detail.el.matches(data.collisionEntities)) { return }\r\n collisionEntities.push(evt.detail.el)\r\n }\r\n el.sceneEl.addEventListener('child-attached', this.childAttachHandler)\r\n\r\n // Update entity list on detach.\r\n this.childDetachHandler = function childDetachHandler (evt) {\r\n if (!evt.detail.el.matches(data.collisionEntities)) { return }\r\n const index = collisionEntities.indexOf(evt.detail.el)\r\n if (index === -1) { return }\r\n collisionEntities.splice(index, 1)\r\n }\r\n el.sceneEl.addEventListener('child-detached', this.childDetachHandler)\r\n },\r\n\r\n onButtonDown: function () {\r\n this.active = true\r\n this.redrawLine = true\r\n },\r\n\r\n /**\r\n * Jump!\r\n */\r\n onButtonUp: (function () {\r\n const newRigLocalPosition = new THREE.Vector3()\r\n const newHandPosition = [new THREE.Vector3(), new THREE.Vector3()] // Left and right\r\n const handPosition = new THREE.Vector3()\r\n\r\n return function (evt) {\r\n if (!this.active) { return }\r\n\r\n // Hide the hit point and the curve\r\n this.active = false\r\n this.hitEntity.setAttribute('visible', false)\r\n this.teleportEntity.setAttribute('visible', false)\r\n\r\n if (!this.hit) {\r\n // Button released but no hit point\r\n return\r\n }\r\n\r\n const rig = this.data.cameraRig || this.el.sceneEl.camera.el\r\n rig.object3D.getWorldPosition(this.rigWorldPosition)\r\n this.newRigWorldPosition.copy(this.hitPoint)\r\n\r\n // Finally update the rigs position\r\n newRigLocalPosition.copy(this.newRigWorldPosition)\r\n if (rig.object3D.parent) {\r\n rig.object3D.parent.worldToLocal(newRigLocalPosition)\r\n }\r\n rig.setAttribute('position', newRigLocalPosition)\r\n\r\n // Also take the headset/camera rotation itself into account\r\n if (this.data.rotateOnTeleport) {\r\n this.teleportOriginQuaternion\r\n .setFromEuler(new THREE.Euler(0, this.teleportOrigin.object3D.rotation.y, 0))\r\n this.teleportOriginQuaternion.invert()\r\n this.teleportOriginQuaternion.multiply(this.hitEntityQuaternion)\r\n // Rotate the rig based on calculated teleport origin rotation\r\n this.cameraRig.object3D.setRotationFromQuaternion(this.teleportOriginQuaternion)\r\n }\r\n\r\n // If a rig was not explicitly declared, look for hands and move them proportionally as well\r\n if (!this.data.cameraRig) {\r\n const hands = document.querySelectorAll('a-entity[tracked-controls]')\r\n for (let i = 0; i < hands.length; i++) {\r\n hands[i].object3D.getWorldPosition(handPosition)\r\n\r\n // diff = rigWorldPosition - handPosition\r\n // newPos = newRigWorldPosition - diff\r\n newHandPosition[i].copy(this.newRigWorldPosition).sub(this.rigWorldPosition).add(handPosition)\r\n hands[i].setAttribute('position', newHandPosition[i])\r\n }\r\n }\r\n\r\n this.el.emit('teleported', this.teleportEventDetail)\r\n }\r\n })(),\r\n\r\n cancel: function () {\r\n this.active = false\r\n this.hitEntity.setAttribute('visible', false)\r\n this.teleportEntity.setAttribute('visible', false)\r\n },\r\n\r\n /**\r\n * Check for raycaster intersection.\r\n *\r\n * @param {number} Line fragment point index.\r\n * @param {number} Last line fragment point index.\r\n * @param {number} Next line fragment point index.\r\n * @returns {boolean} true if there's an intersection.\r\n */\r\n checkMeshCollisions: function (i, last, next) {\r\n // @todo We should add a property to define if the collisionEntity is dynamic or static\r\n // If static we should do the map just once, otherwise we're recreating the array in every\r\n // loop when aiming.\r\n let meshes\r\n if (!this.data.collisionEntities) {\r\n meshes = this.defaultCollisionMeshes\r\n } else {\r\n meshes = this.collisionEntities.map(function (entity) {\r\n return entity.getObject3D('mesh')\r\n }).filter(function (n) { return n })\r\n meshes = meshes.length ? meshes : this.defaultCollisionMeshes\r\n }\r\n\r\n const intersects = this.raycaster.intersectObjects(meshes, true)\r\n if (intersects.length > 0 && !this.hit &&\r\n this.isValidNormalsAngle(intersects[0].face.normal, intersects[0].object)) {\r\n const point = intersects[0].point\r\n\r\n this.line.material.color.set(this.curveHitColor)\r\n this.line.material.opacity = this.data.hitOpacity\r\n this.line.material.transparent = this.data.hitOpacity < 1\r\n this.hitEntity.setAttribute('position', point)\r\n this.hitEntity.setAttribute('visible', true)\r\n\r\n this.hit = true\r\n this.hitPoint.copy(intersects[0].point)\r\n\r\n // If hit, just fill the rest of the points with the hit point and break the loop\r\n for (let j = i; j < this.line.numPoints; j++) {\r\n this.line.setPoint(j, last, this.hitPoint)\r\n }\r\n return true\r\n } else {\r\n this.line.setPoint(i, last, next)\r\n return false\r\n }\r\n },\r\n\r\n isValidNormalsAngle: function (collisionNormal, collisionObject) {\r\n this.collisionObjectNormalMatrix.getNormalMatrix(collisionObject.matrixWorld)\r\n this.collisionWorldNormal.copy(collisionNormal)\r\n .applyMatrix3(this.collisionObjectNormalMatrix).normalize()\r\n const angleNormals = this.referenceNormal.angleTo(this.collisionWorldNormal)\r\n return (THREE.Math.RAD2DEG * angleNormals <= this.data.landingMaxAngle)\r\n },\r\n\r\n // Utils\r\n // Parabolic motion equation, y = p0 + v0*t + 1/2at^2\r\n parabolicCurveScalar: function (p0, v0, a, t) {\r\n return p0 + v0 * t + 0.5 * a * t * t\r\n },\r\n\r\n // Parabolic motion equation applied to 3 dimensions\r\n parabolicCurve: function (p0, v0, a, t, out) {\r\n out.x = this.parabolicCurveScalar(p0.x, v0.x, a.x, t)\r\n out.y = this.parabolicCurveScalar(p0.y, v0.y, a.y, t)\r\n out.z = this.parabolicCurveScalar(p0.z, v0.z, a.z, t)\r\n return out\r\n },\r\n\r\n // To determine how long in terms of t we need to calculate\r\n parabolicCurveMaxRoot: function (p0, v0, a) {\r\n const root = (-v0.y - Math.sqrt(v0.y ** 2 - 4 * (0.5 * a.y) * p0.y)) / (2 * 0.5 * a.y)\r\n return root\r\n },\r\n\r\n createLine: function (data) {\r\n const numPoints = data.type === 'line' ? 2 : data.curveNumberPoints\r\n return new AFRAME.utils.RayCurve(numPoints, data.curveLineWidth)\r\n },\r\n\r\n /**\r\n * Create mesh to represent the area of intersection.\r\n * Default to a combination of torus and cylinder.\r\n */\r\n createHitEntity: function (data) {\r\n // Parent.\r\n const hitEntity = document.createElement('a-entity')\r\n hitEntity.className = 'hitEntity'\r\n\r\n // Torus.\r\n const torus = document.createElement('a-entity')\r\n torus.setAttribute('geometry', {\r\n primitive: 'torus',\r\n radius: data.hitCylinderRadius,\r\n radiusTubular: 0.01\r\n })\r\n torus.setAttribute('rotation', { x: 90, y: 0, z: 0 })\r\n torus.setAttribute('material', {\r\n shader: 'flat',\r\n color: data.hitCylinderColor,\r\n side: 'double',\r\n depthTest: false\r\n })\r\n hitEntity.appendChild(torus)\r\n\r\n // Cylinder.\r\n const cylinder = document.createElement('a-entity')\r\n cylinder.setAttribute('position', { x: 0, y: data.hitCylinderHeight / 2, z: 0 })\r\n cylinder.setAttribute('geometry', {\r\n primitive: 'cylinder',\r\n segmentsHeight: 1,\r\n radius: data.hitCylinderRadius,\r\n height: data.hitCylinderHeight,\r\n openEnded: true\r\n })\r\n cylinder.setAttribute('material', {\r\n shader: 'flat',\r\n color: data.hitCylinderColor,\r\n opacity: 0.5,\r\n side: 'double',\r\n src: this.cylinderTexture,\r\n transparent: true,\r\n depthTest: false\r\n })\r\n hitEntity.appendChild(cylinder)\r\n\r\n const pointer = document.createElement('a-entity')\r\n pointer.setAttribute('position', { x: 0, y: 0.05, z: data.hitCylinderRadius * -1.5 })\r\n pointer.setAttribute('rotation', { x: 90, y: 180, z: 0 })\r\n pointer.setAttribute('geometry', {\r\n primitive: 'prism',\r\n height: 0.2,\r\n width: 0.2,\r\n depth: 0.05\r\n })\r\n pointer.setAttribute('material', {\r\n shader: 'flat',\r\n color: data.hitCylinderColor,\r\n side: 'double',\r\n transparent: true,\r\n opacity: 0.6,\r\n depthTest: false\r\n })\r\n hitEntity.appendChild(pointer)\r\n\r\n return hitEntity\r\n },\r\n createDefaultPlane: function (size) {\r\n const geometry = new THREE.PlaneGeometry(100, 100)\r\n geometry.rotateX(-Math.PI / 2)\r\n const material = new THREE.MeshBasicMaterial({ color: 0xffff00 })\r\n return new THREE.Mesh(geometry, material)\r\n },\r\n cylinderTexture: 'url(data:image/png;base64,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)'\r\n})\r\n\r\nAFRAME.utils.RayCurve = function (numPoints, width) {\r\n this.geometry = new THREE.BufferGeometry()\r\n this.vertices = new Float32Array(numPoints * 3 * 6) // 6 vertices (2 triangles) * 3 dimensions\r\n this.uvs = new Float32Array(numPoints * 2 * 6) // 2 uvs per vertex\r\n this.width = width\r\n\r\n this.geometry.setAttribute('position', new THREE.BufferAttribute(this.vertices, 3).setUsage(THREE.DynamicDrawUsage))\r\n\r\n this.material = new THREE.MeshBasicMaterial({\r\n side: THREE.DoubleSide,\r\n color: 0xff0000\r\n })\r\n\r\n this.mesh = new THREE.Mesh(this.geometry, this.material)\r\n\r\n this.mesh.frustumCulled = false\r\n this.mesh.vertices = this.vertices\r\n\r\n this.direction = new THREE.Vector3()\r\n this.numPoints = numPoints\r\n}\r\n\r\nAFRAME.utils.RayCurve.prototype = {\r\n setDirection: function (direction) {\r\n const UP = new THREE.Vector3(0, 1, 0)\r\n this.direction\r\n .copy(direction)\r\n .cross(UP)\r\n .normalize()\r\n .multiplyScalar(this.width / 2)\r\n },\r\n\r\n setWidth: function (width) {\r\n this.width = width\r\n },\r\n\r\n setPoint: (function () {\r\n const posA = new THREE.Vector3()\r\n const posB = new THREE.Vector3()\r\n const posC = new THREE.Vector3()\r\n const posD = new THREE.Vector3()\r\n\r\n return function (i, last, next) {\r\n posA.copy(last).add(this.direction)\r\n posB.copy(last).sub(this.direction)\r\n\r\n posC.copy(next).add(this.direction)\r\n posD.copy(next).sub(this.direction)\r\n\r\n let idx = 6 * 3 * i // 6 vertices per point\r\n\r\n this.vertices[idx++] = posA.x\r\n this.vertices[idx++] = posA.y\r\n this.vertices[idx++] = posA.z\r\n\r\n this.vertices[idx++] = posB.x\r\n this.vertices[idx++] = posB.y\r\n this.vertices[idx++] = posB.z\r\n\r\n this.vertices[idx++] = posC.x\r\n this.vertices[idx++] = posC.y\r\n this.vertices[idx++] = posC.z\r\n\r\n this.vertices[idx++] = posC.x\r\n this.vertices[idx++] = posC.y\r\n this.vertices[idx++] = posC.z\r\n\r\n this.vertices[idx++] = posB.x\r\n this.vertices[idx++] = posB.y\r\n this.vertices[idx++] = posB.z\r\n\r\n this.vertices[idx++] = posD.x\r\n this.vertices[idx++] = posD.y\r\n this.vertices[idx++] = posD.z\r\n\r\n this.geometry.attributes.position.needsUpdate = true\r\n }\r\n })()\r\n}\r\n"],"names":["AFRAME","registerGeometry","schema","depth","default","min","height","width","init","data","shape","THREE","Shape","moveTo","lineTo","extrudeSettings","steps","bevelEnabled","this","geometry","ExtrudeGeometry","Error","registerComponent","button","oneOf","startEvents","type","endEvents","cancelEvents","collisionEntities","hitEntity","cameraRig","teleportOrigin","hitCylinderColor","hitCylinderRadius","hitCylinderHeight","interval","curveNumberPoints","curveLineWidth","curveHitColor","curveMissColor","curveShootingSpeed","defaultPlaneSize","landingNormal","x","y","z","landingMaxAngle","max","drawIncrementally","incrementalDrawMs","missOpacity","hitOpacity","snapTurn","rotateOnTeleport","el","i","active","obj","object3D","controllerPosition","Vector3","hitEntityQuaternion","Quaternion","teleportOriginQuaternion","hitPoint","collisionObjectNormalMatrix","Matrix3","collisionWorldNormal","rigWorldPosition","newRigWorldPosition","teleportEventDetail","oldPosition","newPosition","rotationQuaternion","hit","prevCheckTime","undefined","referenceNormal","Color","raycaster","Raycaster","defaultPlane","createDefaultPlane","defaultCollisionMeshes","teleportEntity","document","createElement","classList","add","setAttribute","sceneEl","appendChild","onButtonDown","bind","onButtonUp","cancel","handleThumbstickAxis","snapturnRotation","MathUtils","degToRad","canSnapturn","addedEvents","length","push","addEventListener","thumbstickAxisActivation","queryCollisionEntities","handleSnapturn","rotation","strength","Math","abs","PI","rotateY","evt","detail","atan2","sqrt","getWorldPosition","setComponent","position","visible","lookAt","getWorldQuaternion","update","oldData","diff","utils","copy","set","line","createLine","material","opacity","transparent","numActivePoints","setObject3D","mesh","parentNode","removeChild","createHitEntity","lastElementChild","remove","removeEventListener","childAttachHandler","childDetachHandler","name","fn","tick","p0","v0","a","next","last","quaternion","translation","scale","shootAngle","lastNext","auxDirection","timeSinceDrawStart","time","delta","redrawLine","matrixWorld","decompose","direction","applyQuaternion","normalize","setDirection","color","multiplyScalar","lastDrawnIndex","numPoints","t","floor","timeToReach0","parabolicCurveMaxRoot","parabolicCurve","dirLastNext","sub","far","lastDrawnPoint","checkMeshCollisions","j","setPoint","slice","call","querySelectorAll","matches","index","indexOf","splice","newRigLocalPosition","newHandPosition","handPosition","rig","camera","parent","worldToLocal","setFromEuler","Euler","invert","multiply","setRotationFromQuaternion","hands","emit","meshes","map","entity","getObject3D","filter","n","intersects","intersectObjects","isValidNormalsAngle","face","normal","object","point","collisionNormal","collisionObject","getNormalMatrix","applyMatrix3","angleNormals","angleTo","RAD2DEG","parabolicCurveScalar","out","RayCurve","className","torus","primitive","radius","radiusTubular","shader","side","depthTest","cylinder","segmentsHeight","openEnded","src","cylinderTexture","pointer","size","PlaneGeometry","rotateX","MeshBasicMaterial","Mesh","BufferGeometry","vertices","Float32Array","uvs","BufferAttribute","setUsage","DynamicDrawUsage","DoubleSide","frustumCulled","prototype","UP","cross","setWidth","posA","posB","posC","posD","idx","attributes","needsUpdate"],"sourceRoot":""} \ No newline at end of file +{"version":3,"file":"dist/aframe-blink-controls.min.js","mappings":"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","sources":["webpack://aframe-blink-controls/./src/index.js"],"sourcesContent":["/* global THREE, AFRAME */\r\n\r\n// Adapted from https://github.com/fernandojsg/aframe-teleport-controls\r\n// Additions: Teleport rotation, parabolic root calculation, bindings, fix for triangle strip draw mode\r\n// Removals: Line teleport\r\n// WARNING: Super early! Currently only tested with Oculus Touch controllers\r\n\r\nAFRAME.registerGeometry('prism', {\r\n schema: {\r\n depth: { default: 1, min: 0 },\r\n height: { default: 1, min: 0 },\r\n width: { default: 1, min: 0 }\r\n },\r\n\r\n init: function (data) {\r\n const shape = new THREE.Shape()\r\n shape.moveTo(data.width / 2, 0)\r\n shape.lineTo(0, data.height)\r\n shape.lineTo(-data.width / 2, 0)\r\n shape.lineTo(data.width / 2, 0)\r\n\r\n const extrudeSettings = {\r\n steps: 2,\r\n depth: data.depth,\r\n bevelEnabled: false\r\n }\r\n this.geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings)\r\n }\r\n})\r\n\r\n// WIP: Controller bindings cheat sheet\r\n// For HTC Vive: trackpaddown and trackpadup with axismove\r\n// For Oculus Touch: thumbstickdown and thumbstickup, with thumbstick event and evt.detail.y and evt.detail.x\r\n// For Valve Index (maybe): touchstart, touchend, axismove?\r\n\r\nif (typeof AFRAME === 'undefined') {\r\n throw new Error('Component attempted to register before AFRAME was available.')\r\n}\r\n\r\nAFRAME.registerComponent('blink-controls', {\r\n schema: {\r\n // Button is a simplified startEvents & endEvents specification, e.g.\r\n // 'thumbstick' binds 'thumbstickdown' and 'thumbstickup' respectively\r\n button: { default: '', oneOf: ['trackpad', 'trigger', 'grip', 'menu', 'thumbstick'] },\r\n // The default teleport activation is a forward thumbstick axis,\r\n // but this can be changed with startEvents.\r\n startEvents: { type: 'array', default: [] },\r\n // The default teleport de-activation is a centered thumbstick axis,\r\n // but this can be changed with endEvents.\r\n endEvents: { type: 'array', default: [] },\r\n // Not assigned by default\r\n cancelEvents: { type: 'array', default: [] },\r\n collisionEntities: { default: '' },\r\n hitEntity: { type: 'selector' },\r\n cameraRig: { type: 'selector', default: '#player' },\r\n teleportOrigin: { type: 'selector', default: '#camera' },\r\n hitCylinderColor: { type: 'color', default: '#4d93fd' },\r\n hitCylinderRadius: { default: 0.25, min: 0 },\r\n hitCylinderHeight: { default: 0.3, min: 0 },\r\n interval: { default: 0 },\r\n curveNumberPoints: { default: 60, min: 2 },\r\n curveLineWidth: { default: 0.025 },\r\n curveHitColor: { type: 'color', default: '#4d93fd' },\r\n curveMissColor: { type: 'color', default: '#ff0000' },\r\n curveShootingSpeed: { default: 10, min: 0 },\r\n defaultPlaneSize: { default: 100 },\r\n landingNormal: { type: 'vec3', default: { x: 0, y: 1, z: 0 } },\r\n landingMaxAngle: { default: '45', min: 0, max: 360 },\r\n drawIncrementally: { default: true },\r\n incrementalDrawMs: { default: 300 },\r\n missOpacity: { default: 0.8 },\r\n hitOpacity: { default: 0.8 },\r\n snapTurn: { default: true },\r\n rotateOnTeleport: { default: true }\r\n },\r\n\r\n init: function () {\r\n const data = this.data\r\n const el = this.el\r\n let i\r\n\r\n this.active = false\r\n this.obj = el.object3D\r\n this.controllerPosition = new THREE.Vector3()\r\n this.hitEntityQuaternion = new THREE.Quaternion()\r\n // teleportOrigin is headset/camera with look-controls\r\n this.teleportOriginQuaternion = new THREE.Quaternion()\r\n this.hitPoint = new THREE.Vector3()\r\n this.collisionObjectNormalMatrix = new THREE.Matrix3()\r\n this.collisionWorldNormal = new THREE.Vector3()\r\n this.rigWorldPosition = new THREE.Vector3()\r\n this.newRigWorldPosition = new THREE.Vector3()\r\n this.teleportEventDetail = {\r\n oldPosition: this.rigWorldPosition,\r\n newPosition: this.newRigWorldPosition,\r\n hitPoint: this.hitPoint,\r\n rotationQuaternion: this.hitEntityQuaternion\r\n }\r\n\r\n this.hit = false\r\n this.prevCheckTime = undefined\r\n this.referenceNormal = new THREE.Vector3()\r\n this.curveMissColor = new THREE.Color()\r\n this.curveHitColor = new THREE.Color()\r\n this.raycaster = new THREE.Raycaster()\r\n\r\n this.defaultPlane = this.createDefaultPlane(this.data.defaultPlaneSize)\r\n this.defaultCollisionMeshes = [this.defaultPlane]\r\n\r\n const teleportEntity = this.teleportEntity = document.createElement('a-entity')\r\n teleportEntity.classList.add('teleportRay')\r\n teleportEntity.setAttribute('visible', false)\r\n el.sceneEl.appendChild(this.teleportEntity)\r\n\r\n this.onButtonDown = this.onButtonDown.bind(this)\r\n this.onButtonUp = this.onButtonUp.bind(this)\r\n this.cancel = this.cancel.bind(this)\r\n this.handleThumbstickAxis = this.handleThumbstickAxis.bind(this)\r\n\r\n this.teleportOrigin = this.data.teleportOrigin\r\n this.cameraRig = this.data.cameraRig\r\n\r\n this.snapturnRotation = THREE.MathUtils.degToRad(45)\r\n this.canSnapturn = true\r\n this.addedEvents = [];\r\n\r\n // Are startEvents and endEvents specified?\r\n if (this.data.startEvents.length && this.data.endEvents.length) {\r\n for (i = 0; i < this.data.startEvents.length; i++) {\r\n this.addedEvents.push([this.data.startEvents[i], this.onButtonDown])\r\n el.addEventListener(this.data.startEvents[i], this.onButtonDown)\r\n }\r\n for (i = 0; i < this.data.endEvents.length; i++) {\r\n this.addedEvents.push([this.data.endEvents[i], this.onButtonUp])\r\n el.addEventListener(this.data.endEvents[i], this.onButtonUp)\r\n }\r\n // Is a button for activation specified?\r\n } else if (data.button) {\r\n this.addedEvents.push([data.button + 'down', this.onButtonDown])\r\n this.addedEvents.push([data.button + 'up', this.onButtonUp])\r\n el.addEventListener(data.button + 'down', this.onButtonDown)\r\n el.addEventListener(data.button + 'up', this.onButtonUp)\r\n // If none of the above, default to thumbstick-axis based activation\r\n } else {\r\n this.thumbstickAxisActivation = true\r\n }\r\n \r\n for (i = 0; i < this.data.cancelEvents.length; i++) {\r\n this.addedEvents.push([this.data.cancelEvents[i], this.cancel])\r\n el.addEventListener(this.data.cancelEvents[i], this.cancel)\r\n }\r\n\r\n this.addedEvents.push(['thumbstickmoved', this.handleThumbstickAxis])\r\n el.addEventListener('thumbstickmoved', this.handleThumbstickAxis)\r\n this.queryCollisionEntities()\r\n },\r\n handleSnapturn: function (rotation, strength) {\r\n if (strength < 0.50) this.canSnapturn = true\r\n if (!this.canSnapturn) return\r\n // Only do snapturns if axis is very prominent (user intent is clear)\r\n // And preven further snapturns until axis returns to (close enough to) 0\r\n if (strength > 0.95) {\r\n if (Math.abs(rotation - Math.PI / 2.0) < 0.6) {\r\n this.cameraRig.object3D.rotateY(+this.snapturnRotation)\r\n this.canSnapturn = false\r\n } else if (Math.abs(rotation - 1.5 * Math.PI) < 0.6) {\r\n this.cameraRig.object3D.rotateY(-this.snapturnRotation)\r\n this.canSnapturn = false\r\n }\r\n }\r\n // if (rotation ) {\r\n // this.cameraRig.object3D.rotateY(-Math.sign(x) * this.snapturnRotation)\r\n // this.canSnapturn = false\r\n // }\r\n },\r\n handleThumbstickAxis: function (evt) {\r\n if (evt.detail.x !== undefined && evt.detail.y !== undefined) {\r\n const rotation = Math.atan2(evt.detail.x, evt.detail.y) + Math.PI\r\n const strength = Math.sqrt(evt.detail.x ** 2 + evt.detail.y ** 2)\r\n\r\n if (this.active) {\r\n // Only rotate if the axes are sufficiently prominent,\r\n // to prevent rotating in undesired/fluctuating directions.\r\n if (strength > 0.95) {\r\n this.obj.getWorldPosition(this.controllerPosition)\r\n this.controllerPosition.setComponent(1, this.hitEntity.object3D.position.y)\r\n // TODO: We set hitEntity invisible to prevent rotation glitches\r\n // but we could also rotate an invisible object instead and only\r\n // apply the final rotation to hitEntity.\r\n this.hitEntity.object3D.visible = false\r\n this.hitEntity.object3D.lookAt(this.controllerPosition)\r\n this.hitEntity.object3D.rotateY(rotation)\r\n this.hitEntity.object3D.visible = true\r\n this.hitEntity.object3D.getWorldQuaternion(this.hitEntityQuaternion)\r\n }\r\n if (Math.abs(evt.detail.x) === 0 && Math.abs(evt.detail.y) === 0) {\r\n // Disable teleport on axis return to 0 if axis (de)activation is enabled\r\n this.onButtonUp()\r\n }\r\n // Forward (rotation 0.0 || 6.28 is straight ahead)\r\n // We use half a radian left and right for some leeway\r\n // We also check for significant y axis movement to prevent\r\n // accidental teleports\r\n } else if (this.thumbstickAxisActivation && strength > 0.95 && (rotation < 0.50 || rotation > 5.78)) {\r\n // Activate (fuzzily) on forward axis if axis activation is enabled\r\n this.onButtonDown()\r\n } else if (this.data.snapTurn) {\r\n this.handleSnapturn(rotation, strength)\r\n }\r\n }\r\n },\r\n update: function (oldData) {\r\n const data = this.data\r\n const diff = AFRAME.utils.diff(data, oldData)\r\n\r\n // Update normal.\r\n this.referenceNormal.copy(data.landingNormal)\r\n\r\n // Update colors.\r\n this.curveMissColor.set(data.curveMissColor)\r\n this.curveHitColor.set(data.curveHitColor)\r\n\r\n // Create or update line mesh.\r\n if (!this.line ||\r\n 'curveLineWidth' in diff || 'curveNumberPoints' in diff || 'type' in diff) {\r\n this.line = this.createLine(data)\r\n this.line.material.opacity = this.data.hitOpacity\r\n this.line.material.transparent = this.data.hitOpacity < 1\r\n this.numActivePoints = data.curveNumberPoints\r\n this.teleportEntity.setObject3D('mesh', this.line.mesh)\r\n }\r\n\r\n // Create or update hit entity.\r\n if (data.hitEntity) {\r\n this.hitEntity = data.hitEntity\r\n } else if (!this.hitEntity || 'hitCylinderColor' in diff || 'hitCylinderHeight' in diff ||\r\n 'hitCylinderRadius' in diff) {\r\n // Remove previous entity, create new entity (could be more performant).\r\n if (this.hitEntity) { this.hitEntity.parentNode.removeChild(this.hitEntity) }\r\n this.hitEntity = this.createHitEntity(data)\r\n this.el.sceneEl.appendChild(this.hitEntity)\r\n }\r\n this.hitEntity.setAttribute('visible', false)\r\n\r\n // If it has rotation on teleport disabled hide the arrow indicating the teleportation direction \r\n if (!data.hitEntity) {\r\n this.hitEntity.lastElementChild.setAttribute('visible', data.rotateOnTeleport);\r\n }\r\n\r\n if ('collisionEntities' in diff) { this.queryCollisionEntities() }\r\n },\r\n\r\n remove: function () {\r\n const el = this.el\r\n const hitEntity = this.hitEntity\r\n const teleportEntity = this.teleportEntity\r\n\r\n if (hitEntity) { hitEntity.parentNode.removeChild(hitEntity) }\r\n if (teleportEntity) { teleportEntity.parentNode.removeChild(teleportEntity) }\r\n\r\n el.sceneEl.removeEventListener('child-attached', this.childAttachHandler)\r\n el.sceneEl.removeEventListener('child-detached', this.childDetachHandler)\r\n\r\n // Clean up event listeners if component removed but element isn't\r\n for (const [name, fn] of this.addedEvents) {\r\n el.removeEventListener(name, fn);\r\n }\r\n },\r\n\r\n tick: (function () {\r\n const p0 = new THREE.Vector3()\r\n const v0 = new THREE.Vector3()\r\n const g = -9.8\r\n const a = new THREE.Vector3(0, g, 0)\r\n const next = new THREE.Vector3()\r\n const last = new THREE.Vector3()\r\n const quaternion = new THREE.Quaternion()\r\n const translation = new THREE.Vector3()\r\n const scale = new THREE.Vector3()\r\n const shootAngle = new THREE.Vector3()\r\n const lastNext = new THREE.Vector3()\r\n const auxDirection = new THREE.Vector3()\r\n let timeSinceDrawStart = 0\r\n\r\n return function (time, delta) {\r\n if (!this.active) { return }\r\n if (this.data.drawIncrementally && this.redrawLine) {\r\n this.redrawLine = false\r\n timeSinceDrawStart = 0\r\n }\r\n timeSinceDrawStart += delta\r\n this.numActivePoints = this.data.curveNumberPoints * timeSinceDrawStart / this.data.incrementalDrawMs\r\n if (this.numActivePoints > this.data.curveNumberPoints) {\r\n this.numActivePoints = this.data.curveNumberPoints\r\n }\r\n\r\n // Only check for intersection if interval time has passed.\r\n if (this.prevCheckTime && (time - this.prevCheckTime < this.data.interval)) { return }\r\n // Update check time.\r\n this.prevCheckTime = time\r\n\r\n const matrixWorld = this.obj.matrixWorld\r\n matrixWorld.decompose(translation, quaternion, scale)\r\n\r\n const direction = shootAngle.set(0, 0, -1)\r\n .applyQuaternion(quaternion).normalize()\r\n this.line.setDirection(auxDirection.copy(direction))\r\n this.obj.getWorldPosition(p0)\r\n\r\n last.copy(p0)\r\n\r\n // Set default status as non-hit\r\n this.teleportEntity.setAttribute('visible', true)\r\n\r\n // But use hit color until ray animation finishes\r\n if (timeSinceDrawStart < this.data.incrementalDrawMs) {\r\n this.line.material.color.set(this.curveHitColor)\r\n } else {\r\n this.line.material.color.set(this.curveMissColor)\r\n }\r\n this.line.material.opacity = this.data.missOpacity\r\n this.line.material.transparent = this.data.missOpacity < 1\r\n this.hitEntity.setAttribute('visible', false)\r\n this.hit = false\r\n\r\n v0.copy(direction).multiplyScalar(this.data.curveShootingSpeed)\r\n\r\n this.lastDrawnIndex = 0\r\n const numPoints = this.data.drawIncrementally ? this.numActivePoints : this.line.numPoints\r\n for (let i = 0; i < numPoints + 1; i++) {\r\n let t\r\n if (i === Math.floor(numPoints + 1)) {\r\n t = numPoints / (this.line.numPoints - 1)\r\n } else {\r\n t = i / (this.line.numPoints - 1)\r\n }\r\n const timeToReach0 = this.parabolicCurveMaxRoot(p0, v0, a)\r\n t = t * Math.max(1, 1.5 * timeToReach0)\r\n\r\n this.parabolicCurve(p0, v0, a, t, next)\r\n // Update the raycaster with the length of the current segment last->next\r\n const dirLastNext = lastNext.copy(next).sub(last).normalize()\r\n this.raycaster.far = dirLastNext.length()\r\n this.raycaster.set(last, dirLastNext)\r\n\r\n this.lastDrawnPoint = next\r\n this.lastDrawnIndex = i\r\n if (this.checkMeshCollisions(i, last, next)) { break }\r\n\r\n last.copy(next)\r\n }\r\n for (let j = this.lastDrawnIndex + 1; j < this.line.numPoints; j++) {\r\n this.line.setPoint(j, this.lastDrawnPoint, this.lastDrawnPoint)\r\n }\r\n }\r\n })(),\r\n\r\n /**\r\n * Run `querySelectorAll` for `collisionEntities` and maintain it with `child-attached`\r\n * and `child-detached` events.\r\n */\r\n queryCollisionEntities: function () {\r\n const data = this.data\r\n const el = this.el\r\n\r\n if (!data.collisionEntities) {\r\n this.collisionEntities = []\r\n return\r\n }\r\n\r\n const collisionEntities = [].slice.call(el.sceneEl.querySelectorAll(data.collisionEntities))\r\n this.collisionEntities = collisionEntities\r\n\r\n // Update entity list on attach.\r\n this.childAttachHandler = function childAttachHandler (evt) {\r\n if (!evt.detail.el.matches(data.collisionEntities)) { return }\r\n collisionEntities.push(evt.detail.el)\r\n }\r\n el.sceneEl.addEventListener('child-attached', this.childAttachHandler)\r\n\r\n // Update entity list on detach.\r\n this.childDetachHandler = function childDetachHandler (evt) {\r\n if (!evt.detail.el.matches(data.collisionEntities)) { return }\r\n const index = collisionEntities.indexOf(evt.detail.el)\r\n if (index === -1) { return }\r\n collisionEntities.splice(index, 1)\r\n }\r\n el.sceneEl.addEventListener('child-detached', this.childDetachHandler)\r\n },\r\n\r\n onButtonDown: function () {\r\n this.active = true\r\n this.redrawLine = true\r\n },\r\n\r\n /**\r\n * Jump!\r\n */\r\n onButtonUp: (function () {\r\n const newRigLocalPosition = new THREE.Vector3()\r\n const newHandPosition = [new THREE.Vector3(), new THREE.Vector3()] // Left and right\r\n const handPosition = new THREE.Vector3()\r\n\r\n return function (evt) {\r\n if (!this.active) { return }\r\n\r\n // Hide the hit point and the curve\r\n this.active = false\r\n this.hitEntity.setAttribute('visible', false)\r\n this.teleportEntity.setAttribute('visible', false)\r\n\r\n if (!this.hit) {\r\n // Button released but no hit point\r\n return\r\n }\r\n\r\n const rig = this.data.cameraRig || this.el.sceneEl.camera.el\r\n rig.object3D.getWorldPosition(this.rigWorldPosition)\r\n this.newRigWorldPosition.copy(this.hitPoint)\r\n\r\n // Finally update the rigs position\r\n newRigLocalPosition.copy(this.newRigWorldPosition)\r\n if (rig.object3D.parent) {\r\n rig.object3D.parent.worldToLocal(newRigLocalPosition)\r\n }\r\n rig.setAttribute('position', newRigLocalPosition)\r\n\r\n // Also take the headset/camera rotation itself into account\r\n if (this.data.rotateOnTeleport) {\r\n this.teleportOriginQuaternion\r\n .setFromEuler(new THREE.Euler(0, this.teleportOrigin.object3D.rotation.y, 0))\r\n this.teleportOriginQuaternion.invert()\r\n this.teleportOriginQuaternion.multiply(this.hitEntityQuaternion)\r\n // Rotate the rig based on calculated teleport origin rotation\r\n this.cameraRig.object3D.setRotationFromQuaternion(this.teleportOriginQuaternion)\r\n }\r\n\r\n // If a rig was not explicitly declared, look for hands and move them proportionally as well\r\n if (!this.data.cameraRig) {\r\n const hands = document.querySelectorAll('a-entity[tracked-controls]')\r\n for (let i = 0; i < hands.length; i++) {\r\n hands[i].object3D.getWorldPosition(handPosition)\r\n\r\n // diff = rigWorldPosition - handPosition\r\n // newPos = newRigWorldPosition - diff\r\n newHandPosition[i].copy(this.newRigWorldPosition).sub(this.rigWorldPosition).add(handPosition)\r\n hands[i].setAttribute('position', newHandPosition[i])\r\n }\r\n }\r\n\r\n this.el.emit('teleported', this.teleportEventDetail)\r\n }\r\n })(),\r\n\r\n cancel: function () {\r\n this.active = false\r\n this.hitEntity.setAttribute('visible', false)\r\n this.teleportEntity.setAttribute('visible', false)\r\n },\r\n\r\n /**\r\n * Check for raycaster intersection.\r\n *\r\n * @param {number} Line fragment point index.\r\n * @param {number} Last line fragment point index.\r\n * @param {number} Next line fragment point index.\r\n * @returns {boolean} true if there's an intersection.\r\n */\r\n checkMeshCollisions: function (i, last, next) {\r\n // @todo We should add a property to define if the collisionEntity is dynamic or static\r\n // If static we should do the map just once, otherwise we're recreating the array in every\r\n // loop when aiming.\r\n let meshes\r\n if (!this.data.collisionEntities) {\r\n meshes = this.defaultCollisionMeshes\r\n } else {\r\n meshes = this.collisionEntities.map(function (entity) {\r\n return entity.getObject3D('mesh')\r\n }).filter(function (n) { return n })\r\n meshes = meshes.length ? meshes : this.defaultCollisionMeshes\r\n }\r\n\r\n const intersects = this.raycaster.intersectObjects(meshes, true)\r\n if (intersects.length > 0 && !this.hit &&\r\n this.isValidNormalsAngle(intersects[0].face.normal, intersects[0].object)) {\r\n const point = intersects[0].point\r\n\r\n this.line.material.color.set(this.curveHitColor)\r\n this.line.material.opacity = this.data.hitOpacity\r\n this.line.material.transparent = this.data.hitOpacity < 1\r\n this.hitEntity.setAttribute('position', point)\r\n this.hitEntity.setAttribute('visible', true)\r\n\r\n this.hit = true\r\n this.hitPoint.copy(intersects[0].point)\r\n\r\n // If hit, just fill the rest of the points with the hit point and break the loop\r\n for (let j = i; j < this.line.numPoints; j++) {\r\n this.line.setPoint(j, last, this.hitPoint)\r\n }\r\n return true\r\n } else {\r\n this.line.setPoint(i, last, next)\r\n return false\r\n }\r\n },\r\n\r\n isValidNormalsAngle: function (collisionNormal, collisionObject) {\r\n this.collisionObjectNormalMatrix.getNormalMatrix(collisionObject.matrixWorld)\r\n this.collisionWorldNormal.copy(collisionNormal)\r\n .applyMatrix3(this.collisionObjectNormalMatrix).normalize()\r\n const angleNormals = this.referenceNormal.angleTo(this.collisionWorldNormal)\r\n return (THREE.MathUtils.RAD2DEG * angleNormals <= this.data.landingMaxAngle)\r\n },\r\n\r\n // Utils\r\n // Parabolic motion equation, y = p0 + v0*t + 1/2at^2\r\n parabolicCurveScalar: function (p0, v0, a, t) {\r\n return p0 + v0 * t + 0.5 * a * t * t\r\n },\r\n\r\n // Parabolic motion equation applied to 3 dimensions\r\n parabolicCurve: function (p0, v0, a, t, out) {\r\n out.x = this.parabolicCurveScalar(p0.x, v0.x, a.x, t)\r\n out.y = this.parabolicCurveScalar(p0.y, v0.y, a.y, t)\r\n out.z = this.parabolicCurveScalar(p0.z, v0.z, a.z, t)\r\n return out\r\n },\r\n\r\n // To determine how long in terms of t we need to calculate\r\n parabolicCurveMaxRoot: function (p0, v0, a) {\r\n const root = (-v0.y - Math.sqrt(v0.y ** 2 - 4 * (0.5 * a.y) * p0.y)) / (2 * 0.5 * a.y)\r\n return root\r\n },\r\n\r\n createLine: function (data) {\r\n const numPoints = data.type === 'line' ? 2 : data.curveNumberPoints\r\n return new AFRAME.utils.RayCurve(numPoints, data.curveLineWidth)\r\n },\r\n\r\n /**\r\n * Create mesh to represent the area of intersection.\r\n * Default to a combination of torus and cylinder.\r\n */\r\n createHitEntity: function (data) {\r\n // Parent.\r\n const hitEntity = document.createElement('a-entity')\r\n hitEntity.className = 'hitEntity'\r\n\r\n // Torus.\r\n const torus = document.createElement('a-entity')\r\n torus.setAttribute('geometry', {\r\n primitive: 'torus',\r\n radius: data.hitCylinderRadius,\r\n radiusTubular: 0.01\r\n })\r\n torus.setAttribute('rotation', { x: 90, y: 0, z: 0 })\r\n torus.setAttribute('material', {\r\n shader: 'flat',\r\n color: data.hitCylinderColor,\r\n side: 'double',\r\n depthTest: false\r\n })\r\n hitEntity.appendChild(torus)\r\n\r\n // Cylinder.\r\n const cylinder = document.createElement('a-entity')\r\n cylinder.setAttribute('position', { x: 0, y: data.hitCylinderHeight / 2, z: 0 })\r\n cylinder.setAttribute('geometry', {\r\n primitive: 'cylinder',\r\n segmentsHeight: 1,\r\n radius: data.hitCylinderRadius,\r\n height: data.hitCylinderHeight,\r\n openEnded: true\r\n })\r\n cylinder.setAttribute('material', {\r\n shader: 'flat',\r\n color: data.hitCylinderColor,\r\n opacity: 0.5,\r\n side: 'double',\r\n src: this.cylinderTexture,\r\n transparent: true,\r\n depthTest: false\r\n })\r\n hitEntity.appendChild(cylinder)\r\n\r\n const pointer = document.createElement('a-entity')\r\n pointer.setAttribute('position', { x: 0, y: 0.05, z: data.hitCylinderRadius * -1.5 })\r\n pointer.setAttribute('rotation', { x: 90, y: 180, z: 0 })\r\n pointer.setAttribute('geometry', {\r\n primitive: 'prism',\r\n height: 0.2,\r\n width: 0.2,\r\n depth: 0.05\r\n })\r\n pointer.setAttribute('material', {\r\n shader: 'flat',\r\n color: data.hitCylinderColor,\r\n side: 'double',\r\n transparent: true,\r\n opacity: 0.6,\r\n depthTest: false\r\n })\r\n hitEntity.appendChild(pointer)\r\n\r\n return hitEntity\r\n },\r\n createDefaultPlane: function (size) {\r\n const geometry = new THREE.PlaneGeometry(100, 100)\r\n geometry.rotateX(-Math.PI / 2)\r\n const material = new THREE.MeshBasicMaterial({ color: 0xffff00 })\r\n return new THREE.Mesh(geometry, material)\r\n },\r\n cylinderTexture: 'url(data:image/png;base64,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)'\r\n})\r\n\r\nAFRAME.utils.RayCurve = function (numPoints, width) {\r\n this.geometry = new THREE.BufferGeometry()\r\n this.vertices = new Float32Array(numPoints * 3 * 6) // 6 vertices (2 triangles) * 3 dimensions\r\n this.uvs = new Float32Array(numPoints * 2 * 6) // 2 uvs per vertex\r\n this.width = width\r\n\r\n this.geometry.setAttribute('position', new THREE.BufferAttribute(this.vertices, 3).setUsage(THREE.DynamicDrawUsage))\r\n\r\n this.material = new THREE.MeshBasicMaterial({\r\n side: THREE.DoubleSide,\r\n color: 0xff0000\r\n })\r\n\r\n this.mesh = new THREE.Mesh(this.geometry, this.material)\r\n\r\n this.mesh.frustumCulled = false\r\n this.mesh.vertices = this.vertices\r\n\r\n this.direction = new THREE.Vector3()\r\n this.numPoints = numPoints\r\n}\r\n\r\nAFRAME.utils.RayCurve.prototype = {\r\n setDirection: function (direction) {\r\n const UP = new THREE.Vector3(0, 1, 0)\r\n this.direction\r\n .copy(direction)\r\n .cross(UP)\r\n .normalize()\r\n .multiplyScalar(this.width / 2)\r\n },\r\n\r\n setWidth: function (width) {\r\n this.width = width\r\n },\r\n\r\n setPoint: (function () {\r\n const posA = new THREE.Vector3()\r\n const posB = new THREE.Vector3()\r\n const posC = new THREE.Vector3()\r\n const posD = new THREE.Vector3()\r\n\r\n return function (i, last, next) {\r\n posA.copy(last).add(this.direction)\r\n posB.copy(last).sub(this.direction)\r\n\r\n posC.copy(next).add(this.direction)\r\n posD.copy(next).sub(this.direction)\r\n\r\n let idx = 6 * 3 * i // 6 vertices per point\r\n\r\n this.vertices[idx++] = posA.x\r\n this.vertices[idx++] = posA.y\r\n this.vertices[idx++] = posA.z\r\n\r\n this.vertices[idx++] = posB.x\r\n this.vertices[idx++] = posB.y\r\n this.vertices[idx++] = posB.z\r\n\r\n this.vertices[idx++] = posC.x\r\n this.vertices[idx++] = posC.y\r\n this.vertices[idx++] = posC.z\r\n\r\n this.vertices[idx++] = posC.x\r\n this.vertices[idx++] = posC.y\r\n this.vertices[idx++] = posC.z\r\n\r\n this.vertices[idx++] = posB.x\r\n this.vertices[idx++] = posB.y\r\n this.vertices[idx++] = posB.z\r\n\r\n this.vertices[idx++] = posD.x\r\n this.vertices[idx++] = posD.y\r\n this.vertices[idx++] = posD.z\r\n\r\n this.geometry.attributes.position.needsUpdate = true\r\n }\r\n })()\r\n}\r\n"],"names":["AFRAME","registerGeometry","schema","depth","default","min","height","width","init","data","shape","THREE","Shape","moveTo","lineTo","extrudeSettings","steps","bevelEnabled","this","geometry","ExtrudeGeometry","Error","registerComponent","button","oneOf","startEvents","type","endEvents","cancelEvents","collisionEntities","hitEntity","cameraRig","teleportOrigin","hitCylinderColor","hitCylinderRadius","hitCylinderHeight","interval","curveNumberPoints","curveLineWidth","curveHitColor","curveMissColor","curveShootingSpeed","defaultPlaneSize","landingNormal","x","y","z","landingMaxAngle","max","drawIncrementally","incrementalDrawMs","missOpacity","hitOpacity","snapTurn","rotateOnTeleport","el","i","active","obj","object3D","controllerPosition","Vector3","hitEntityQuaternion","Quaternion","teleportOriginQuaternion","hitPoint","collisionObjectNormalMatrix","Matrix3","collisionWorldNormal","rigWorldPosition","newRigWorldPosition","teleportEventDetail","oldPosition","newPosition","rotationQuaternion","hit","prevCheckTime","undefined","referenceNormal","Color","raycaster","Raycaster","defaultPlane","createDefaultPlane","defaultCollisionMeshes","teleportEntity","document","createElement","classList","add","setAttribute","sceneEl","appendChild","onButtonDown","bind","onButtonUp","cancel","handleThumbstickAxis","snapturnRotation","MathUtils","degToRad","canSnapturn","addedEvents","length","push","addEventListener","thumbstickAxisActivation","queryCollisionEntities","handleSnapturn","rotation","strength","Math","abs","PI","rotateY","evt","detail","atan2","sqrt","getWorldPosition","setComponent","position","visible","lookAt","getWorldQuaternion","update","oldData","diff","utils","copy","set","line","createLine","material","opacity","transparent","numActivePoints","setObject3D","mesh","parentNode","removeChild","createHitEntity","lastElementChild","remove","removeEventListener","childAttachHandler","childDetachHandler","name","fn","tick","p0","v0","a","next","last","quaternion","translation","scale","shootAngle","lastNext","auxDirection","timeSinceDrawStart","time","delta","redrawLine","matrixWorld","decompose","direction","applyQuaternion","normalize","setDirection","color","multiplyScalar","lastDrawnIndex","numPoints","t","floor","timeToReach0","parabolicCurveMaxRoot","parabolicCurve","dirLastNext","sub","far","lastDrawnPoint","checkMeshCollisions","j","setPoint","slice","call","querySelectorAll","matches","index","indexOf","splice","newRigLocalPosition","newHandPosition","handPosition","rig","camera","parent","worldToLocal","setFromEuler","Euler","invert","multiply","setRotationFromQuaternion","hands","emit","meshes","map","entity","getObject3D","filter","n","intersects","intersectObjects","isValidNormalsAngle","face","normal","object","point","collisionNormal","collisionObject","getNormalMatrix","applyMatrix3","angleNormals","angleTo","RAD2DEG","parabolicCurveScalar","out","RayCurve","className","torus","primitive","radius","radiusTubular","shader","side","depthTest","cylinder","segmentsHeight","openEnded","src","cylinderTexture","pointer","size","PlaneGeometry","rotateX","MeshBasicMaterial","Mesh","BufferGeometry","vertices","Float32Array","uvs","BufferAttribute","setUsage","DynamicDrawUsage","DoubleSide","frustumCulled","prototype","UP","cross","setWidth","posA","posB","posC","posD","idx","attributes","needsUpdate"],"sourceRoot":""} \ No newline at end of file diff --git a/docs/dist/aframe-blink-controls.js b/docs/dist/aframe-blink-controls.js index 04448e0..16346e8 100644 --- a/docs/dist/aframe-blink-controls.js +++ b/docs/dist/aframe-blink-controls.js @@ -512,7 +512,7 @@ AFRAME.registerComponent('blink-controls', { this.collisionWorldNormal.copy(collisionNormal) .applyMatrix3(this.collisionObjectNormalMatrix).normalize() const angleNormals = this.referenceNormal.angleTo(this.collisionWorldNormal) - return (THREE.Math.RAD2DEG * angleNormals <= this.data.landingMaxAngle) + return (THREE.MathUtils.RAD2DEG * angleNormals <= this.data.landingMaxAngle) }, // Utils diff --git a/docs/dist/aframe-blink-controls.js.map b/docs/dist/aframe-blink-controls.js.map index f4b9e14..b5f71c2 100644 --- a/docs/dist/aframe-blink-controls.js.map +++ b/docs/dist/aframe-blink-controls.js.map @@ -1 +1 @@ -{"version":3,"file":"docs/dist/aframe-blink-controls.js","mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,oBAAoB;AACjC,cAAc,oBAAoB;AAClC,aAAa;AACb,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,2EAA2E;AACzF;AACA;AACA,mBAAmB,4BAA4B;AAC/C;AACA;AACA,iBAAiB,4BAA4B;AAC7C;AACA,oBAAoB,4BAA4B;AAChD,yBAAyB,aAAa;AACtC,iBAAiB,kBAAkB;AACnC,iBAAiB,sCAAsC;AACvD,sBAAsB,sCAAsC;AAC5D,wBAAwB,mCAAmC;AAC3D,yBAAyB,uBAAuB;AAChD,yBAAyB,sBAAsB;AAC/C,gBAAgB,YAAY;AAC5B,yBAAyB,qBAAqB;AAC9C,sBAAsB,gBAAgB;AACtC,qBAAqB,mCAAmC;AACxD,sBAAsB,mCAAmC;AACzD,0BAA0B,qBAAqB;AAC/C,wBAAwB,cAAc;AACtC,qBAAqB,yBAAyB,oBAAoB;AAClE,uBAAuB,iCAAiC;AACxD,yBAAyB,eAAe;AACxC,yBAAyB,cAAc;AACvC,mBAAmB,cAAc;AACjC,kBAAkB,cAAc;AAChC,gBAAgB,eAAe;AAC/B,wBAAwB;AACxB,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kBAAkB,kCAAkC;AACpD;AACA;AACA;AACA,kBAAkB,gCAAgC;AAClD;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA,gBAAgB,mCAAmC;AACnD;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,QAAQ;AACR;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,QAAQ;AACR;AACA;AACA,QAAQ;AACR;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA,4BAA4B;AAC5B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,uCAAuC;AACvC,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB,0BAA0B;AAC1B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,0BAA0B;AAC1B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,oFAAoF;AACpF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,QAAQ;AACR;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,sBAAsB,mBAAmB;AACzC;AACA;AACA;AACA,UAAU;AACV;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,uDAAuD;AACvD;AACA;AACA;AACA,4CAA4C,yBAAyB;AACrE;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,4DAA4D;AAC5D;AACA;AACA;AACA;AACA;AACA;AACA,4DAA4D;AAC5D;AACA,0BAA0B;AAC1B;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,0BAA0B;AAC1B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,wBAAwB,kBAAkB;AAC1C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA,aAAa,QAAQ;AACrB,aAAa,QAAQ;AACrB,aAAa,QAAQ;AACrB,eAAe,SAAS;AACxB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA,OAAO,wBAAwB,UAAU;AACzC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,sBAAsB,yBAAyB;AAC/C;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL,qCAAqC,mBAAmB;AACxD;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA,wCAAwC,2CAA2C;AACnF;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA,uCAAuC,iDAAiD;AACxF,uCAAuC,qBAAqB;AAC5D;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA,mDAAmD,iBAAiB;AACpE;AACA,GAAG;AACH,uCAAuC;AACvC,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH","sources":["webpack://aframe-blink-controls/./src/index.js"],"sourcesContent":["/* global THREE, AFRAME */\r\n\r\n// Adapted from https://github.com/fernandojsg/aframe-teleport-controls\r\n// Additions: Teleport rotation, parabolic root calculation, bindings, fix for triangle strip draw mode\r\n// Removals: Line teleport\r\n// WARNING: Super early! Currently only tested with Oculus Touch controllers\r\n\r\nAFRAME.registerGeometry('prism', {\r\n schema: {\r\n depth: { default: 1, min: 0 },\r\n height: { default: 1, min: 0 },\r\n width: { default: 1, min: 0 }\r\n },\r\n\r\n init: function (data) {\r\n const shape = new THREE.Shape()\r\n shape.moveTo(data.width / 2, 0)\r\n shape.lineTo(0, data.height)\r\n shape.lineTo(-data.width / 2, 0)\r\n shape.lineTo(data.width / 2, 0)\r\n\r\n const extrudeSettings = {\r\n steps: 2,\r\n depth: data.depth,\r\n bevelEnabled: false\r\n }\r\n this.geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings)\r\n }\r\n})\r\n\r\n// WIP: Controller bindings cheat sheet\r\n// For HTC Vive: trackpaddown and trackpadup with axismove\r\n// For Oculus Touch: thumbstickdown and thumbstickup, with thumbstick event and evt.detail.y and evt.detail.x\r\n// For Valve Index (maybe): touchstart, touchend, axismove?\r\n\r\nif (typeof AFRAME === 'undefined') {\r\n throw new Error('Component attempted to register before AFRAME was available.')\r\n}\r\n\r\nAFRAME.registerComponent('blink-controls', {\r\n schema: {\r\n // Button is a simplified startEvents & endEvents specification, e.g.\r\n // 'thumbstick' binds 'thumbstickdown' and 'thumbstickup' respectively\r\n button: { default: '', oneOf: ['trackpad', 'trigger', 'grip', 'menu', 'thumbstick'] },\r\n // The default teleport activation is a forward thumbstick axis,\r\n // but this can be changed with startEvents.\r\n startEvents: { type: 'array', default: [] },\r\n // The default teleport de-activation is a centered thumbstick axis,\r\n // but this can be changed with endEvents.\r\n endEvents: { type: 'array', default: [] },\r\n // Not assigned by default\r\n cancelEvents: { type: 'array', default: [] },\r\n collisionEntities: { default: '' },\r\n hitEntity: { type: 'selector' },\r\n cameraRig: { type: 'selector', default: '#player' },\r\n teleportOrigin: { type: 'selector', default: '#camera' },\r\n hitCylinderColor: { type: 'color', default: '#4d93fd' },\r\n hitCylinderRadius: { default: 0.25, min: 0 },\r\n hitCylinderHeight: { default: 0.3, min: 0 },\r\n interval: { default: 0 },\r\n curveNumberPoints: { default: 60, min: 2 },\r\n curveLineWidth: { default: 0.025 },\r\n curveHitColor: { type: 'color', default: '#4d93fd' },\r\n curveMissColor: { type: 'color', default: '#ff0000' },\r\n curveShootingSpeed: { default: 10, min: 0 },\r\n defaultPlaneSize: { default: 100 },\r\n landingNormal: { type: 'vec3', default: { x: 0, y: 1, z: 0 } },\r\n landingMaxAngle: { default: '45', min: 0, max: 360 },\r\n drawIncrementally: { default: true },\r\n incrementalDrawMs: { default: 300 },\r\n missOpacity: { default: 0.8 },\r\n hitOpacity: { default: 0.8 },\r\n snapTurn: { default: true },\r\n rotateOnTeleport: { default: true }\r\n },\r\n\r\n init: function () {\r\n const data = this.data\r\n const el = this.el\r\n let i\r\n\r\n this.active = false\r\n this.obj = el.object3D\r\n this.controllerPosition = new THREE.Vector3()\r\n this.hitEntityQuaternion = new THREE.Quaternion()\r\n // teleportOrigin is headset/camera with look-controls\r\n this.teleportOriginQuaternion = new THREE.Quaternion()\r\n this.hitPoint = new THREE.Vector3()\r\n this.collisionObjectNormalMatrix = new THREE.Matrix3()\r\n this.collisionWorldNormal = new THREE.Vector3()\r\n this.rigWorldPosition = new THREE.Vector3()\r\n this.newRigWorldPosition = new THREE.Vector3()\r\n this.teleportEventDetail = {\r\n oldPosition: this.rigWorldPosition,\r\n newPosition: this.newRigWorldPosition,\r\n hitPoint: this.hitPoint,\r\n rotationQuaternion: this.hitEntityQuaternion\r\n }\r\n\r\n this.hit = false\r\n this.prevCheckTime = undefined\r\n this.referenceNormal = new THREE.Vector3()\r\n this.curveMissColor = new THREE.Color()\r\n this.curveHitColor = new THREE.Color()\r\n this.raycaster = new THREE.Raycaster()\r\n\r\n this.defaultPlane = this.createDefaultPlane(this.data.defaultPlaneSize)\r\n this.defaultCollisionMeshes = [this.defaultPlane]\r\n\r\n const teleportEntity = this.teleportEntity = document.createElement('a-entity')\r\n teleportEntity.classList.add('teleportRay')\r\n teleportEntity.setAttribute('visible', false)\r\n el.sceneEl.appendChild(this.teleportEntity)\r\n\r\n this.onButtonDown = this.onButtonDown.bind(this)\r\n this.onButtonUp = this.onButtonUp.bind(this)\r\n this.cancel = this.cancel.bind(this)\r\n this.handleThumbstickAxis = this.handleThumbstickAxis.bind(this)\r\n\r\n this.teleportOrigin = this.data.teleportOrigin\r\n this.cameraRig = this.data.cameraRig\r\n\r\n this.snapturnRotation = THREE.MathUtils.degToRad(45)\r\n this.canSnapturn = true\r\n this.addedEvents = [];\r\n\r\n // Are startEvents and endEvents specified?\r\n if (this.data.startEvents.length && this.data.endEvents.length) {\r\n for (i = 0; i < this.data.startEvents.length; i++) {\r\n this.addedEvents.push([this.data.startEvents[i], this.onButtonDown])\r\n el.addEventListener(this.data.startEvents[i], this.onButtonDown)\r\n }\r\n for (i = 0; i < this.data.endEvents.length; i++) {\r\n this.addedEvents.push([this.data.endEvents[i], this.onButtonUp])\r\n el.addEventListener(this.data.endEvents[i], this.onButtonUp)\r\n }\r\n // Is a button for activation specified?\r\n } else if (data.button) {\r\n this.addedEvents.push([data.button + 'down', this.onButtonDown])\r\n this.addedEvents.push([data.button + 'up', this.onButtonUp])\r\n el.addEventListener(data.button + 'down', this.onButtonDown)\r\n el.addEventListener(data.button + 'up', this.onButtonUp)\r\n // If none of the above, default to thumbstick-axis based activation\r\n } else {\r\n this.thumbstickAxisActivation = true\r\n }\r\n \r\n for (i = 0; i < this.data.cancelEvents.length; i++) {\r\n this.addedEvents.push([this.data.cancelEvents[i], this.cancel])\r\n el.addEventListener(this.data.cancelEvents[i], this.cancel)\r\n }\r\n\r\n this.addedEvents.push(['thumbstickmoved', this.handleThumbstickAxis])\r\n el.addEventListener('thumbstickmoved', this.handleThumbstickAxis)\r\n this.queryCollisionEntities()\r\n },\r\n handleSnapturn: function (rotation, strength) {\r\n if (strength < 0.50) this.canSnapturn = true\r\n if (!this.canSnapturn) return\r\n // Only do snapturns if axis is very prominent (user intent is clear)\r\n // And preven further snapturns until axis returns to (close enough to) 0\r\n if (strength > 0.95) {\r\n if (Math.abs(rotation - Math.PI / 2.0) < 0.6) {\r\n this.cameraRig.object3D.rotateY(+this.snapturnRotation)\r\n this.canSnapturn = false\r\n } else if (Math.abs(rotation - 1.5 * Math.PI) < 0.6) {\r\n this.cameraRig.object3D.rotateY(-this.snapturnRotation)\r\n this.canSnapturn = false\r\n }\r\n }\r\n // if (rotation ) {\r\n // this.cameraRig.object3D.rotateY(-Math.sign(x) * this.snapturnRotation)\r\n // this.canSnapturn = false\r\n // }\r\n },\r\n handleThumbstickAxis: function (evt) {\r\n if (evt.detail.x !== undefined && evt.detail.y !== undefined) {\r\n const rotation = Math.atan2(evt.detail.x, evt.detail.y) + Math.PI\r\n const strength = Math.sqrt(evt.detail.x ** 2 + evt.detail.y ** 2)\r\n\r\n if (this.active) {\r\n // Only rotate if the axes are sufficiently prominent,\r\n // to prevent rotating in undesired/fluctuating directions.\r\n if (strength > 0.95) {\r\n this.obj.getWorldPosition(this.controllerPosition)\r\n this.controllerPosition.setComponent(1, this.hitEntity.object3D.position.y)\r\n // TODO: We set hitEntity invisible to prevent rotation glitches\r\n // but we could also rotate an invisible object instead and only\r\n // apply the final rotation to hitEntity.\r\n this.hitEntity.object3D.visible = false\r\n this.hitEntity.object3D.lookAt(this.controllerPosition)\r\n this.hitEntity.object3D.rotateY(rotation)\r\n this.hitEntity.object3D.visible = true\r\n this.hitEntity.object3D.getWorldQuaternion(this.hitEntityQuaternion)\r\n }\r\n if (Math.abs(evt.detail.x) === 0 && Math.abs(evt.detail.y) === 0) {\r\n // Disable teleport on axis return to 0 if axis (de)activation is enabled\r\n this.onButtonUp()\r\n }\r\n // Forward (rotation 0.0 || 6.28 is straight ahead)\r\n // We use half a radian left and right for some leeway\r\n // We also check for significant y axis movement to prevent\r\n // accidental teleports\r\n } else if (this.thumbstickAxisActivation && strength > 0.95 && (rotation < 0.50 || rotation > 5.78)) {\r\n // Activate (fuzzily) on forward axis if axis activation is enabled\r\n this.onButtonDown()\r\n } else if (this.data.snapTurn) {\r\n this.handleSnapturn(rotation, strength)\r\n }\r\n }\r\n },\r\n update: function (oldData) {\r\n const data = this.data\r\n const diff = AFRAME.utils.diff(data, oldData)\r\n\r\n // Update normal.\r\n this.referenceNormal.copy(data.landingNormal)\r\n\r\n // Update colors.\r\n this.curveMissColor.set(data.curveMissColor)\r\n this.curveHitColor.set(data.curveHitColor)\r\n\r\n // Create or update line mesh.\r\n if (!this.line ||\r\n 'curveLineWidth' in diff || 'curveNumberPoints' in diff || 'type' in diff) {\r\n this.line = this.createLine(data)\r\n this.line.material.opacity = this.data.hitOpacity\r\n this.line.material.transparent = this.data.hitOpacity < 1\r\n this.numActivePoints = data.curveNumberPoints\r\n this.teleportEntity.setObject3D('mesh', this.line.mesh)\r\n }\r\n\r\n // Create or update hit entity.\r\n if (data.hitEntity) {\r\n this.hitEntity = data.hitEntity\r\n } else if (!this.hitEntity || 'hitCylinderColor' in diff || 'hitCylinderHeight' in diff ||\r\n 'hitCylinderRadius' in diff) {\r\n // Remove previous entity, create new entity (could be more performant).\r\n if (this.hitEntity) { this.hitEntity.parentNode.removeChild(this.hitEntity) }\r\n this.hitEntity = this.createHitEntity(data)\r\n this.el.sceneEl.appendChild(this.hitEntity)\r\n }\r\n this.hitEntity.setAttribute('visible', false)\r\n\r\n // If it has rotation on teleport disabled hide the arrow indicating the teleportation direction \r\n if (!data.hitEntity) {\r\n this.hitEntity.lastElementChild.setAttribute('visible', data.rotateOnTeleport);\r\n }\r\n\r\n if ('collisionEntities' in diff) { this.queryCollisionEntities() }\r\n },\r\n\r\n remove: function () {\r\n const el = this.el\r\n const hitEntity = this.hitEntity\r\n const teleportEntity = this.teleportEntity\r\n\r\n if (hitEntity) { hitEntity.parentNode.removeChild(hitEntity) }\r\n if (teleportEntity) { teleportEntity.parentNode.removeChild(teleportEntity) }\r\n\r\n el.sceneEl.removeEventListener('child-attached', this.childAttachHandler)\r\n el.sceneEl.removeEventListener('child-detached', this.childDetachHandler)\r\n\r\n // Clean up event listeners if component removed but element isn't\r\n for (const [name, fn] of this.addedEvents) {\r\n el.removeEventListener(name, fn);\r\n }\r\n },\r\n\r\n tick: (function () {\r\n const p0 = new THREE.Vector3()\r\n const v0 = new THREE.Vector3()\r\n const g = -9.8\r\n const a = new THREE.Vector3(0, g, 0)\r\n const next = new THREE.Vector3()\r\n const last = new THREE.Vector3()\r\n const quaternion = new THREE.Quaternion()\r\n const translation = new THREE.Vector3()\r\n const scale = new THREE.Vector3()\r\n const shootAngle = new THREE.Vector3()\r\n const lastNext = new THREE.Vector3()\r\n const auxDirection = new THREE.Vector3()\r\n let timeSinceDrawStart = 0\r\n\r\n return function (time, delta) {\r\n if (!this.active) { return }\r\n if (this.data.drawIncrementally && this.redrawLine) {\r\n this.redrawLine = false\r\n timeSinceDrawStart = 0\r\n }\r\n timeSinceDrawStart += delta\r\n this.numActivePoints = this.data.curveNumberPoints * timeSinceDrawStart / this.data.incrementalDrawMs\r\n if (this.numActivePoints > this.data.curveNumberPoints) {\r\n this.numActivePoints = this.data.curveNumberPoints\r\n }\r\n\r\n // Only check for intersection if interval time has passed.\r\n if (this.prevCheckTime && (time - this.prevCheckTime < this.data.interval)) { return }\r\n // Update check time.\r\n this.prevCheckTime = time\r\n\r\n const matrixWorld = this.obj.matrixWorld\r\n matrixWorld.decompose(translation, quaternion, scale)\r\n\r\n const direction = shootAngle.set(0, 0, -1)\r\n .applyQuaternion(quaternion).normalize()\r\n this.line.setDirection(auxDirection.copy(direction))\r\n this.obj.getWorldPosition(p0)\r\n\r\n last.copy(p0)\r\n\r\n // Set default status as non-hit\r\n this.teleportEntity.setAttribute('visible', true)\r\n\r\n // But use hit color until ray animation finishes\r\n if (timeSinceDrawStart < this.data.incrementalDrawMs) {\r\n this.line.material.color.set(this.curveHitColor)\r\n } else {\r\n this.line.material.color.set(this.curveMissColor)\r\n }\r\n this.line.material.opacity = this.data.missOpacity\r\n this.line.material.transparent = this.data.missOpacity < 1\r\n this.hitEntity.setAttribute('visible', false)\r\n this.hit = false\r\n\r\n v0.copy(direction).multiplyScalar(this.data.curveShootingSpeed)\r\n\r\n this.lastDrawnIndex = 0\r\n const numPoints = this.data.drawIncrementally ? this.numActivePoints : this.line.numPoints\r\n for (let i = 0; i < numPoints + 1; i++) {\r\n let t\r\n if (i === Math.floor(numPoints + 1)) {\r\n t = numPoints / (this.line.numPoints - 1)\r\n } else {\r\n t = i / (this.line.numPoints - 1)\r\n }\r\n const timeToReach0 = this.parabolicCurveMaxRoot(p0, v0, a)\r\n t = t * Math.max(1, 1.5 * timeToReach0)\r\n\r\n this.parabolicCurve(p0, v0, a, t, next)\r\n // Update the raycaster with the length of the current segment last->next\r\n const dirLastNext = lastNext.copy(next).sub(last).normalize()\r\n this.raycaster.far = dirLastNext.length()\r\n this.raycaster.set(last, dirLastNext)\r\n\r\n this.lastDrawnPoint = next\r\n this.lastDrawnIndex = i\r\n if (this.checkMeshCollisions(i, last, next)) { break }\r\n\r\n last.copy(next)\r\n }\r\n for (let j = this.lastDrawnIndex + 1; j < this.line.numPoints; j++) {\r\n this.line.setPoint(j, this.lastDrawnPoint, this.lastDrawnPoint)\r\n }\r\n }\r\n })(),\r\n\r\n /**\r\n * Run `querySelectorAll` for `collisionEntities` and maintain it with `child-attached`\r\n * and `child-detached` events.\r\n */\r\n queryCollisionEntities: function () {\r\n const data = this.data\r\n const el = this.el\r\n\r\n if (!data.collisionEntities) {\r\n this.collisionEntities = []\r\n return\r\n }\r\n\r\n const collisionEntities = [].slice.call(el.sceneEl.querySelectorAll(data.collisionEntities))\r\n this.collisionEntities = collisionEntities\r\n\r\n // Update entity list on attach.\r\n this.childAttachHandler = function childAttachHandler (evt) {\r\n if (!evt.detail.el.matches(data.collisionEntities)) { return }\r\n collisionEntities.push(evt.detail.el)\r\n }\r\n el.sceneEl.addEventListener('child-attached', this.childAttachHandler)\r\n\r\n // Update entity list on detach.\r\n this.childDetachHandler = function childDetachHandler (evt) {\r\n if (!evt.detail.el.matches(data.collisionEntities)) { return }\r\n const index = collisionEntities.indexOf(evt.detail.el)\r\n if (index === -1) { return }\r\n collisionEntities.splice(index, 1)\r\n }\r\n el.sceneEl.addEventListener('child-detached', this.childDetachHandler)\r\n },\r\n\r\n onButtonDown: function () {\r\n this.active = true\r\n this.redrawLine = true\r\n },\r\n\r\n /**\r\n * Jump!\r\n */\r\n onButtonUp: (function () {\r\n const newRigLocalPosition = new THREE.Vector3()\r\n const newHandPosition = [new THREE.Vector3(), new THREE.Vector3()] // Left and right\r\n const handPosition = new THREE.Vector3()\r\n\r\n return function (evt) {\r\n if (!this.active) { return }\r\n\r\n // Hide the hit point and the curve\r\n this.active = false\r\n this.hitEntity.setAttribute('visible', false)\r\n this.teleportEntity.setAttribute('visible', false)\r\n\r\n if (!this.hit) {\r\n // Button released but no hit point\r\n return\r\n }\r\n\r\n const rig = this.data.cameraRig || this.el.sceneEl.camera.el\r\n rig.object3D.getWorldPosition(this.rigWorldPosition)\r\n this.newRigWorldPosition.copy(this.hitPoint)\r\n\r\n // Finally update the rigs position\r\n newRigLocalPosition.copy(this.newRigWorldPosition)\r\n if (rig.object3D.parent) {\r\n rig.object3D.parent.worldToLocal(newRigLocalPosition)\r\n }\r\n rig.setAttribute('position', newRigLocalPosition)\r\n\r\n // Also take the headset/camera rotation itself into account\r\n if (this.data.rotateOnTeleport) {\r\n this.teleportOriginQuaternion\r\n .setFromEuler(new THREE.Euler(0, this.teleportOrigin.object3D.rotation.y, 0))\r\n this.teleportOriginQuaternion.invert()\r\n this.teleportOriginQuaternion.multiply(this.hitEntityQuaternion)\r\n // Rotate the rig based on calculated teleport origin rotation\r\n this.cameraRig.object3D.setRotationFromQuaternion(this.teleportOriginQuaternion)\r\n }\r\n\r\n // If a rig was not explicitly declared, look for hands and move them proportionally as well\r\n if (!this.data.cameraRig) {\r\n const hands = document.querySelectorAll('a-entity[tracked-controls]')\r\n for (let i = 0; i < hands.length; i++) {\r\n hands[i].object3D.getWorldPosition(handPosition)\r\n\r\n // diff = rigWorldPosition - handPosition\r\n // newPos = newRigWorldPosition - diff\r\n newHandPosition[i].copy(this.newRigWorldPosition).sub(this.rigWorldPosition).add(handPosition)\r\n hands[i].setAttribute('position', newHandPosition[i])\r\n }\r\n }\r\n\r\n this.el.emit('teleported', this.teleportEventDetail)\r\n }\r\n })(),\r\n\r\n cancel: function () {\r\n this.active = false\r\n this.hitEntity.setAttribute('visible', false)\r\n this.teleportEntity.setAttribute('visible', false)\r\n },\r\n\r\n /**\r\n * Check for raycaster intersection.\r\n *\r\n * @param {number} Line fragment point index.\r\n * @param {number} Last line fragment point index.\r\n * @param {number} Next line fragment point index.\r\n * @returns {boolean} true if there's an intersection.\r\n */\r\n checkMeshCollisions: function (i, last, next) {\r\n // @todo We should add a property to define if the collisionEntity is dynamic or static\r\n // If static we should do the map just once, otherwise we're recreating the array in every\r\n // loop when aiming.\r\n let meshes\r\n if (!this.data.collisionEntities) {\r\n meshes = this.defaultCollisionMeshes\r\n } else {\r\n meshes = this.collisionEntities.map(function (entity) {\r\n return entity.getObject3D('mesh')\r\n }).filter(function (n) { return n })\r\n meshes = meshes.length ? meshes : this.defaultCollisionMeshes\r\n }\r\n\r\n const intersects = this.raycaster.intersectObjects(meshes, true)\r\n if (intersects.length > 0 && !this.hit &&\r\n this.isValidNormalsAngle(intersects[0].face.normal, intersects[0].object)) {\r\n const point = intersects[0].point\r\n\r\n this.line.material.color.set(this.curveHitColor)\r\n this.line.material.opacity = this.data.hitOpacity\r\n this.line.material.transparent = this.data.hitOpacity < 1\r\n this.hitEntity.setAttribute('position', point)\r\n this.hitEntity.setAttribute('visible', true)\r\n\r\n this.hit = true\r\n this.hitPoint.copy(intersects[0].point)\r\n\r\n // If hit, just fill the rest of the points with the hit point and break the loop\r\n for (let j = i; j < this.line.numPoints; j++) {\r\n this.line.setPoint(j, last, this.hitPoint)\r\n }\r\n return true\r\n } else {\r\n this.line.setPoint(i, last, next)\r\n return false\r\n }\r\n },\r\n\r\n isValidNormalsAngle: function (collisionNormal, collisionObject) {\r\n this.collisionObjectNormalMatrix.getNormalMatrix(collisionObject.matrixWorld)\r\n this.collisionWorldNormal.copy(collisionNormal)\r\n .applyMatrix3(this.collisionObjectNormalMatrix).normalize()\r\n const angleNormals = this.referenceNormal.angleTo(this.collisionWorldNormal)\r\n return (THREE.Math.RAD2DEG * angleNormals <= this.data.landingMaxAngle)\r\n },\r\n\r\n // Utils\r\n // Parabolic motion equation, y = p0 + v0*t + 1/2at^2\r\n parabolicCurveScalar: function (p0, v0, a, t) {\r\n return p0 + v0 * t + 0.5 * a * t * t\r\n },\r\n\r\n // Parabolic motion equation applied to 3 dimensions\r\n parabolicCurve: function (p0, v0, a, t, out) {\r\n out.x = this.parabolicCurveScalar(p0.x, v0.x, a.x, t)\r\n out.y = this.parabolicCurveScalar(p0.y, v0.y, a.y, t)\r\n out.z = this.parabolicCurveScalar(p0.z, v0.z, a.z, t)\r\n return out\r\n },\r\n\r\n // To determine how long in terms of t we need to calculate\r\n parabolicCurveMaxRoot: function (p0, v0, a) {\r\n const root = (-v0.y - Math.sqrt(v0.y ** 2 - 4 * (0.5 * a.y) * p0.y)) / (2 * 0.5 * a.y)\r\n return root\r\n },\r\n\r\n createLine: function (data) {\r\n const numPoints = data.type === 'line' ? 2 : data.curveNumberPoints\r\n return new AFRAME.utils.RayCurve(numPoints, data.curveLineWidth)\r\n },\r\n\r\n /**\r\n * Create mesh to represent the area of intersection.\r\n * Default to a combination of torus and cylinder.\r\n */\r\n createHitEntity: function (data) {\r\n // Parent.\r\n const hitEntity = document.createElement('a-entity')\r\n hitEntity.className = 'hitEntity'\r\n\r\n // Torus.\r\n const torus = document.createElement('a-entity')\r\n torus.setAttribute('geometry', {\r\n primitive: 'torus',\r\n radius: data.hitCylinderRadius,\r\n radiusTubular: 0.01\r\n })\r\n torus.setAttribute('rotation', { x: 90, y: 0, z: 0 })\r\n torus.setAttribute('material', {\r\n shader: 'flat',\r\n color: data.hitCylinderColor,\r\n side: 'double',\r\n depthTest: false\r\n })\r\n hitEntity.appendChild(torus)\r\n\r\n // Cylinder.\r\n const cylinder = document.createElement('a-entity')\r\n cylinder.setAttribute('position', { x: 0, y: data.hitCylinderHeight / 2, z: 0 })\r\n cylinder.setAttribute('geometry', {\r\n primitive: 'cylinder',\r\n segmentsHeight: 1,\r\n radius: data.hitCylinderRadius,\r\n height: data.hitCylinderHeight,\r\n openEnded: true\r\n })\r\n cylinder.setAttribute('material', {\r\n shader: 'flat',\r\n color: data.hitCylinderColor,\r\n opacity: 0.5,\r\n side: 'double',\r\n src: this.cylinderTexture,\r\n transparent: true,\r\n depthTest: false\r\n })\r\n hitEntity.appendChild(cylinder)\r\n\r\n const pointer = document.createElement('a-entity')\r\n pointer.setAttribute('position', { x: 0, y: 0.05, z: data.hitCylinderRadius * -1.5 })\r\n pointer.setAttribute('rotation', { x: 90, y: 180, z: 0 })\r\n pointer.setAttribute('geometry', {\r\n primitive: 'prism',\r\n height: 0.2,\r\n width: 0.2,\r\n depth: 0.05\r\n })\r\n pointer.setAttribute('material', {\r\n shader: 'flat',\r\n color: data.hitCylinderColor,\r\n side: 'double',\r\n transparent: true,\r\n opacity: 0.6,\r\n depthTest: false\r\n })\r\n hitEntity.appendChild(pointer)\r\n\r\n return hitEntity\r\n },\r\n createDefaultPlane: function (size) {\r\n const geometry = new THREE.PlaneGeometry(100, 100)\r\n geometry.rotateX(-Math.PI / 2)\r\n const material = new THREE.MeshBasicMaterial({ color: 0xffff00 })\r\n return new THREE.Mesh(geometry, material)\r\n },\r\n cylinderTexture: 'url(data:image/png;base64,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)'\r\n})\r\n\r\nAFRAME.utils.RayCurve = function (numPoints, width) {\r\n this.geometry = new THREE.BufferGeometry()\r\n this.vertices = new Float32Array(numPoints * 3 * 6) // 6 vertices (2 triangles) * 3 dimensions\r\n this.uvs = new Float32Array(numPoints * 2 * 6) // 2 uvs per vertex\r\n this.width = width\r\n\r\n this.geometry.setAttribute('position', new THREE.BufferAttribute(this.vertices, 3).setUsage(THREE.DynamicDrawUsage))\r\n\r\n this.material = new THREE.MeshBasicMaterial({\r\n side: THREE.DoubleSide,\r\n color: 0xff0000\r\n })\r\n\r\n this.mesh = new THREE.Mesh(this.geometry, this.material)\r\n\r\n this.mesh.frustumCulled = false\r\n this.mesh.vertices = this.vertices\r\n\r\n this.direction = new THREE.Vector3()\r\n this.numPoints = numPoints\r\n}\r\n\r\nAFRAME.utils.RayCurve.prototype = {\r\n setDirection: function (direction) {\r\n const UP = new THREE.Vector3(0, 1, 0)\r\n this.direction\r\n .copy(direction)\r\n .cross(UP)\r\n .normalize()\r\n .multiplyScalar(this.width / 2)\r\n },\r\n\r\n setWidth: function (width) {\r\n this.width = width\r\n },\r\n\r\n setPoint: (function () {\r\n const posA = new THREE.Vector3()\r\n const posB = new THREE.Vector3()\r\n const posC = new THREE.Vector3()\r\n const posD = new THREE.Vector3()\r\n\r\n return function (i, last, next) {\r\n posA.copy(last).add(this.direction)\r\n posB.copy(last).sub(this.direction)\r\n\r\n posC.copy(next).add(this.direction)\r\n posD.copy(next).sub(this.direction)\r\n\r\n let idx = 6 * 3 * i // 6 vertices per point\r\n\r\n this.vertices[idx++] = posA.x\r\n this.vertices[idx++] = posA.y\r\n this.vertices[idx++] = posA.z\r\n\r\n this.vertices[idx++] = posB.x\r\n this.vertices[idx++] = posB.y\r\n this.vertices[idx++] = posB.z\r\n\r\n this.vertices[idx++] = posC.x\r\n this.vertices[idx++] = posC.y\r\n this.vertices[idx++] = posC.z\r\n\r\n this.vertices[idx++] = posC.x\r\n this.vertices[idx++] = posC.y\r\n this.vertices[idx++] = posC.z\r\n\r\n this.vertices[idx++] = posB.x\r\n this.vertices[idx++] = posB.y\r\n this.vertices[idx++] = posB.z\r\n\r\n this.vertices[idx++] = posD.x\r\n this.vertices[idx++] = posD.y\r\n this.vertices[idx++] = posD.z\r\n\r\n this.geometry.attributes.position.needsUpdate = true\r\n }\r\n })()\r\n}\r\n"],"names":[],"sourceRoot":""} \ No newline at end of file +{"version":3,"file":"docs/dist/aframe-blink-controls.js","mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,oBAAoB;AACjC,cAAc,oBAAoB;AAClC,aAAa;AACb,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,2EAA2E;AACzF;AACA;AACA,mBAAmB,4BAA4B;AAC/C;AACA;AACA,iBAAiB,4BAA4B;AAC7C;AACA,oBAAoB,4BAA4B;AAChD,yBAAyB,aAAa;AACtC,iBAAiB,kBAAkB;AACnC,iBAAiB,sCAAsC;AACvD,sBAAsB,sCAAsC;AAC5D,wBAAwB,mCAAmC;AAC3D,yBAAyB,uBAAuB;AAChD,yBAAyB,sBAAsB;AAC/C,gBAAgB,YAAY;AAC5B,yBAAyB,qBAAqB;AAC9C,sBAAsB,gBAAgB;AACtC,qBAAqB,mCAAmC;AACxD,sBAAsB,mCAAmC;AACzD,0BAA0B,qBAAqB;AAC/C,wBAAwB,cAAc;AACtC,qBAAqB,yBAAyB,oBAAoB;AAClE,uBAAuB,iCAAiC;AACxD,yBAAyB,eAAe;AACxC,yBAAyB,cAAc;AACvC,mBAAmB,cAAc;AACjC,kBAAkB,cAAc;AAChC,gBAAgB,eAAe;AAC/B,wBAAwB;AACxB,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kBAAkB,kCAAkC;AACpD;AACA;AACA;AACA,kBAAkB,gCAAgC;AAClD;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA,gBAAgB,mCAAmC;AACnD;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,QAAQ;AACR;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,QAAQ;AACR;AACA;AACA,QAAQ;AACR;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA,4BAA4B;AAC5B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,uCAAuC;AACvC,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB,0BAA0B;AAC1B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,0BAA0B;AAC1B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,oFAAoF;AACpF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,QAAQ;AACR;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,sBAAsB,mBAAmB;AACzC;AACA;AACA;AACA,UAAU;AACV;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,uDAAuD;AACvD;AACA;AACA;AACA,4CAA4C,yBAAyB;AACrE;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,4DAA4D;AAC5D;AACA;AACA;AACA;AACA;AACA;AACA,4DAA4D;AAC5D;AACA,0BAA0B;AAC1B;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,0BAA0B;AAC1B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,wBAAwB,kBAAkB;AAC1C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA,aAAa,QAAQ;AACrB,aAAa,QAAQ;AACrB,aAAa,QAAQ;AACrB,eAAe,SAAS;AACxB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA,OAAO,wBAAwB,UAAU;AACzC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,sBAAsB,yBAAyB;AAC/C;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL,qCAAqC,mBAAmB;AACxD;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA,wCAAwC,2CAA2C;AACnF;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA,uCAAuC,iDAAiD;AACxF,uCAAuC,qBAAqB;AAC5D;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA,mDAAmD,iBAAiB;AACpE;AACA,GAAG;AACH,uCAAuC;AACvC,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH","sources":["webpack://aframe-blink-controls/./src/index.js"],"sourcesContent":["/* global THREE, AFRAME */\r\n\r\n// Adapted from https://github.com/fernandojsg/aframe-teleport-controls\r\n// Additions: Teleport rotation, parabolic root calculation, bindings, fix for triangle strip draw mode\r\n// Removals: Line teleport\r\n// WARNING: Super early! Currently only tested with Oculus Touch controllers\r\n\r\nAFRAME.registerGeometry('prism', {\r\n schema: {\r\n depth: { default: 1, min: 0 },\r\n height: { default: 1, min: 0 },\r\n width: { default: 1, min: 0 }\r\n },\r\n\r\n init: function (data) {\r\n const shape = new THREE.Shape()\r\n shape.moveTo(data.width / 2, 0)\r\n shape.lineTo(0, data.height)\r\n shape.lineTo(-data.width / 2, 0)\r\n shape.lineTo(data.width / 2, 0)\r\n\r\n const extrudeSettings = {\r\n steps: 2,\r\n depth: data.depth,\r\n bevelEnabled: false\r\n }\r\n this.geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings)\r\n }\r\n})\r\n\r\n// WIP: Controller bindings cheat sheet\r\n// For HTC Vive: trackpaddown and trackpadup with axismove\r\n// For Oculus Touch: thumbstickdown and thumbstickup, with thumbstick event and evt.detail.y and evt.detail.x\r\n// For Valve Index (maybe): touchstart, touchend, axismove?\r\n\r\nif (typeof AFRAME === 'undefined') {\r\n throw new Error('Component attempted to register before AFRAME was available.')\r\n}\r\n\r\nAFRAME.registerComponent('blink-controls', {\r\n schema: {\r\n // Button is a simplified startEvents & endEvents specification, e.g.\r\n // 'thumbstick' binds 'thumbstickdown' and 'thumbstickup' respectively\r\n button: { default: '', oneOf: ['trackpad', 'trigger', 'grip', 'menu', 'thumbstick'] },\r\n // The default teleport activation is a forward thumbstick axis,\r\n // but this can be changed with startEvents.\r\n startEvents: { type: 'array', default: [] },\r\n // The default teleport de-activation is a centered thumbstick axis,\r\n // but this can be changed with endEvents.\r\n endEvents: { type: 'array', default: [] },\r\n // Not assigned by default\r\n cancelEvents: { type: 'array', default: [] },\r\n collisionEntities: { default: '' },\r\n hitEntity: { type: 'selector' },\r\n cameraRig: { type: 'selector', default: '#player' },\r\n teleportOrigin: { type: 'selector', default: '#camera' },\r\n hitCylinderColor: { type: 'color', default: '#4d93fd' },\r\n hitCylinderRadius: { default: 0.25, min: 0 },\r\n hitCylinderHeight: { default: 0.3, min: 0 },\r\n interval: { default: 0 },\r\n curveNumberPoints: { default: 60, min: 2 },\r\n curveLineWidth: { default: 0.025 },\r\n curveHitColor: { type: 'color', default: '#4d93fd' },\r\n curveMissColor: { type: 'color', default: '#ff0000' },\r\n curveShootingSpeed: { default: 10, min: 0 },\r\n defaultPlaneSize: { default: 100 },\r\n landingNormal: { type: 'vec3', default: { x: 0, y: 1, z: 0 } },\r\n landingMaxAngle: { default: '45', min: 0, max: 360 },\r\n drawIncrementally: { default: true },\r\n incrementalDrawMs: { default: 300 },\r\n missOpacity: { default: 0.8 },\r\n hitOpacity: { default: 0.8 },\r\n snapTurn: { default: true },\r\n rotateOnTeleport: { default: true }\r\n },\r\n\r\n init: function () {\r\n const data = this.data\r\n const el = this.el\r\n let i\r\n\r\n this.active = false\r\n this.obj = el.object3D\r\n this.controllerPosition = new THREE.Vector3()\r\n this.hitEntityQuaternion = new THREE.Quaternion()\r\n // teleportOrigin is headset/camera with look-controls\r\n this.teleportOriginQuaternion = new THREE.Quaternion()\r\n this.hitPoint = new THREE.Vector3()\r\n this.collisionObjectNormalMatrix = new THREE.Matrix3()\r\n this.collisionWorldNormal = new THREE.Vector3()\r\n this.rigWorldPosition = new THREE.Vector3()\r\n this.newRigWorldPosition = new THREE.Vector3()\r\n this.teleportEventDetail = {\r\n oldPosition: this.rigWorldPosition,\r\n newPosition: this.newRigWorldPosition,\r\n hitPoint: this.hitPoint,\r\n rotationQuaternion: this.hitEntityQuaternion\r\n }\r\n\r\n this.hit = false\r\n this.prevCheckTime = undefined\r\n this.referenceNormal = new THREE.Vector3()\r\n this.curveMissColor = new THREE.Color()\r\n this.curveHitColor = new THREE.Color()\r\n this.raycaster = new THREE.Raycaster()\r\n\r\n this.defaultPlane = this.createDefaultPlane(this.data.defaultPlaneSize)\r\n this.defaultCollisionMeshes = [this.defaultPlane]\r\n\r\n const teleportEntity = this.teleportEntity = document.createElement('a-entity')\r\n teleportEntity.classList.add('teleportRay')\r\n teleportEntity.setAttribute('visible', false)\r\n el.sceneEl.appendChild(this.teleportEntity)\r\n\r\n this.onButtonDown = this.onButtonDown.bind(this)\r\n this.onButtonUp = this.onButtonUp.bind(this)\r\n this.cancel = this.cancel.bind(this)\r\n this.handleThumbstickAxis = this.handleThumbstickAxis.bind(this)\r\n\r\n this.teleportOrigin = this.data.teleportOrigin\r\n this.cameraRig = this.data.cameraRig\r\n\r\n this.snapturnRotation = THREE.MathUtils.degToRad(45)\r\n this.canSnapturn = true\r\n this.addedEvents = [];\r\n\r\n // Are startEvents and endEvents specified?\r\n if (this.data.startEvents.length && this.data.endEvents.length) {\r\n for (i = 0; i < this.data.startEvents.length; i++) {\r\n this.addedEvents.push([this.data.startEvents[i], this.onButtonDown])\r\n el.addEventListener(this.data.startEvents[i], this.onButtonDown)\r\n }\r\n for (i = 0; i < this.data.endEvents.length; i++) {\r\n this.addedEvents.push([this.data.endEvents[i], this.onButtonUp])\r\n el.addEventListener(this.data.endEvents[i], this.onButtonUp)\r\n }\r\n // Is a button for activation specified?\r\n } else if (data.button) {\r\n this.addedEvents.push([data.button + 'down', this.onButtonDown])\r\n this.addedEvents.push([data.button + 'up', this.onButtonUp])\r\n el.addEventListener(data.button + 'down', this.onButtonDown)\r\n el.addEventListener(data.button + 'up', this.onButtonUp)\r\n // If none of the above, default to thumbstick-axis based activation\r\n } else {\r\n this.thumbstickAxisActivation = true\r\n }\r\n \r\n for (i = 0; i < this.data.cancelEvents.length; i++) {\r\n this.addedEvents.push([this.data.cancelEvents[i], this.cancel])\r\n el.addEventListener(this.data.cancelEvents[i], this.cancel)\r\n }\r\n\r\n this.addedEvents.push(['thumbstickmoved', this.handleThumbstickAxis])\r\n el.addEventListener('thumbstickmoved', this.handleThumbstickAxis)\r\n this.queryCollisionEntities()\r\n },\r\n handleSnapturn: function (rotation, strength) {\r\n if (strength < 0.50) this.canSnapturn = true\r\n if (!this.canSnapturn) return\r\n // Only do snapturns if axis is very prominent (user intent is clear)\r\n // And preven further snapturns until axis returns to (close enough to) 0\r\n if (strength > 0.95) {\r\n if (Math.abs(rotation - Math.PI / 2.0) < 0.6) {\r\n this.cameraRig.object3D.rotateY(+this.snapturnRotation)\r\n this.canSnapturn = false\r\n } else if (Math.abs(rotation - 1.5 * Math.PI) < 0.6) {\r\n this.cameraRig.object3D.rotateY(-this.snapturnRotation)\r\n this.canSnapturn = false\r\n }\r\n }\r\n // if (rotation ) {\r\n // this.cameraRig.object3D.rotateY(-Math.sign(x) * this.snapturnRotation)\r\n // this.canSnapturn = false\r\n // }\r\n },\r\n handleThumbstickAxis: function (evt) {\r\n if (evt.detail.x !== undefined && evt.detail.y !== undefined) {\r\n const rotation = Math.atan2(evt.detail.x, evt.detail.y) + Math.PI\r\n const strength = Math.sqrt(evt.detail.x ** 2 + evt.detail.y ** 2)\r\n\r\n if (this.active) {\r\n // Only rotate if the axes are sufficiently prominent,\r\n // to prevent rotating in undesired/fluctuating directions.\r\n if (strength > 0.95) {\r\n this.obj.getWorldPosition(this.controllerPosition)\r\n this.controllerPosition.setComponent(1, this.hitEntity.object3D.position.y)\r\n // TODO: We set hitEntity invisible to prevent rotation glitches\r\n // but we could also rotate an invisible object instead and only\r\n // apply the final rotation to hitEntity.\r\n this.hitEntity.object3D.visible = false\r\n this.hitEntity.object3D.lookAt(this.controllerPosition)\r\n this.hitEntity.object3D.rotateY(rotation)\r\n this.hitEntity.object3D.visible = true\r\n this.hitEntity.object3D.getWorldQuaternion(this.hitEntityQuaternion)\r\n }\r\n if (Math.abs(evt.detail.x) === 0 && Math.abs(evt.detail.y) === 0) {\r\n // Disable teleport on axis return to 0 if axis (de)activation is enabled\r\n this.onButtonUp()\r\n }\r\n // Forward (rotation 0.0 || 6.28 is straight ahead)\r\n // We use half a radian left and right for some leeway\r\n // We also check for significant y axis movement to prevent\r\n // accidental teleports\r\n } else if (this.thumbstickAxisActivation && strength > 0.95 && (rotation < 0.50 || rotation > 5.78)) {\r\n // Activate (fuzzily) on forward axis if axis activation is enabled\r\n this.onButtonDown()\r\n } else if (this.data.snapTurn) {\r\n this.handleSnapturn(rotation, strength)\r\n }\r\n }\r\n },\r\n update: function (oldData) {\r\n const data = this.data\r\n const diff = AFRAME.utils.diff(data, oldData)\r\n\r\n // Update normal.\r\n this.referenceNormal.copy(data.landingNormal)\r\n\r\n // Update colors.\r\n this.curveMissColor.set(data.curveMissColor)\r\n this.curveHitColor.set(data.curveHitColor)\r\n\r\n // Create or update line mesh.\r\n if (!this.line ||\r\n 'curveLineWidth' in diff || 'curveNumberPoints' in diff || 'type' in diff) {\r\n this.line = this.createLine(data)\r\n this.line.material.opacity = this.data.hitOpacity\r\n this.line.material.transparent = this.data.hitOpacity < 1\r\n this.numActivePoints = data.curveNumberPoints\r\n this.teleportEntity.setObject3D('mesh', this.line.mesh)\r\n }\r\n\r\n // Create or update hit entity.\r\n if (data.hitEntity) {\r\n this.hitEntity = data.hitEntity\r\n } else if (!this.hitEntity || 'hitCylinderColor' in diff || 'hitCylinderHeight' in diff ||\r\n 'hitCylinderRadius' in diff) {\r\n // Remove previous entity, create new entity (could be more performant).\r\n if (this.hitEntity) { this.hitEntity.parentNode.removeChild(this.hitEntity) }\r\n this.hitEntity = this.createHitEntity(data)\r\n this.el.sceneEl.appendChild(this.hitEntity)\r\n }\r\n this.hitEntity.setAttribute('visible', false)\r\n\r\n // If it has rotation on teleport disabled hide the arrow indicating the teleportation direction \r\n if (!data.hitEntity) {\r\n this.hitEntity.lastElementChild.setAttribute('visible', data.rotateOnTeleport);\r\n }\r\n\r\n if ('collisionEntities' in diff) { this.queryCollisionEntities() }\r\n },\r\n\r\n remove: function () {\r\n const el = this.el\r\n const hitEntity = this.hitEntity\r\n const teleportEntity = this.teleportEntity\r\n\r\n if (hitEntity) { hitEntity.parentNode.removeChild(hitEntity) }\r\n if (teleportEntity) { teleportEntity.parentNode.removeChild(teleportEntity) }\r\n\r\n el.sceneEl.removeEventListener('child-attached', this.childAttachHandler)\r\n el.sceneEl.removeEventListener('child-detached', this.childDetachHandler)\r\n\r\n // Clean up event listeners if component removed but element isn't\r\n for (const [name, fn] of this.addedEvents) {\r\n el.removeEventListener(name, fn);\r\n }\r\n },\r\n\r\n tick: (function () {\r\n const p0 = new THREE.Vector3()\r\n const v0 = new THREE.Vector3()\r\n const g = -9.8\r\n const a = new THREE.Vector3(0, g, 0)\r\n const next = new THREE.Vector3()\r\n const last = new THREE.Vector3()\r\n const quaternion = new THREE.Quaternion()\r\n const translation = new THREE.Vector3()\r\n const scale = new THREE.Vector3()\r\n const shootAngle = new THREE.Vector3()\r\n const lastNext = new THREE.Vector3()\r\n const auxDirection = new THREE.Vector3()\r\n let timeSinceDrawStart = 0\r\n\r\n return function (time, delta) {\r\n if (!this.active) { return }\r\n if (this.data.drawIncrementally && this.redrawLine) {\r\n this.redrawLine = false\r\n timeSinceDrawStart = 0\r\n }\r\n timeSinceDrawStart += delta\r\n this.numActivePoints = this.data.curveNumberPoints * timeSinceDrawStart / this.data.incrementalDrawMs\r\n if (this.numActivePoints > this.data.curveNumberPoints) {\r\n this.numActivePoints = this.data.curveNumberPoints\r\n }\r\n\r\n // Only check for intersection if interval time has passed.\r\n if (this.prevCheckTime && (time - this.prevCheckTime < this.data.interval)) { return }\r\n // Update check time.\r\n this.prevCheckTime = time\r\n\r\n const matrixWorld = this.obj.matrixWorld\r\n matrixWorld.decompose(translation, quaternion, scale)\r\n\r\n const direction = shootAngle.set(0, 0, -1)\r\n .applyQuaternion(quaternion).normalize()\r\n this.line.setDirection(auxDirection.copy(direction))\r\n this.obj.getWorldPosition(p0)\r\n\r\n last.copy(p0)\r\n\r\n // Set default status as non-hit\r\n this.teleportEntity.setAttribute('visible', true)\r\n\r\n // But use hit color until ray animation finishes\r\n if (timeSinceDrawStart < this.data.incrementalDrawMs) {\r\n this.line.material.color.set(this.curveHitColor)\r\n } else {\r\n this.line.material.color.set(this.curveMissColor)\r\n }\r\n this.line.material.opacity = this.data.missOpacity\r\n this.line.material.transparent = this.data.missOpacity < 1\r\n this.hitEntity.setAttribute('visible', false)\r\n this.hit = false\r\n\r\n v0.copy(direction).multiplyScalar(this.data.curveShootingSpeed)\r\n\r\n this.lastDrawnIndex = 0\r\n const numPoints = this.data.drawIncrementally ? this.numActivePoints : this.line.numPoints\r\n for (let i = 0; i < numPoints + 1; i++) {\r\n let t\r\n if (i === Math.floor(numPoints + 1)) {\r\n t = numPoints / (this.line.numPoints - 1)\r\n } else {\r\n t = i / (this.line.numPoints - 1)\r\n }\r\n const timeToReach0 = this.parabolicCurveMaxRoot(p0, v0, a)\r\n t = t * Math.max(1, 1.5 * timeToReach0)\r\n\r\n this.parabolicCurve(p0, v0, a, t, next)\r\n // Update the raycaster with the length of the current segment last->next\r\n const dirLastNext = lastNext.copy(next).sub(last).normalize()\r\n this.raycaster.far = dirLastNext.length()\r\n this.raycaster.set(last, dirLastNext)\r\n\r\n this.lastDrawnPoint = next\r\n this.lastDrawnIndex = i\r\n if (this.checkMeshCollisions(i, last, next)) { break }\r\n\r\n last.copy(next)\r\n }\r\n for (let j = this.lastDrawnIndex + 1; j < this.line.numPoints; j++) {\r\n this.line.setPoint(j, this.lastDrawnPoint, this.lastDrawnPoint)\r\n }\r\n }\r\n })(),\r\n\r\n /**\r\n * Run `querySelectorAll` for `collisionEntities` and maintain it with `child-attached`\r\n * and `child-detached` events.\r\n */\r\n queryCollisionEntities: function () {\r\n const data = this.data\r\n const el = this.el\r\n\r\n if (!data.collisionEntities) {\r\n this.collisionEntities = []\r\n return\r\n }\r\n\r\n const collisionEntities = [].slice.call(el.sceneEl.querySelectorAll(data.collisionEntities))\r\n this.collisionEntities = collisionEntities\r\n\r\n // Update entity list on attach.\r\n this.childAttachHandler = function childAttachHandler (evt) {\r\n if (!evt.detail.el.matches(data.collisionEntities)) { return }\r\n collisionEntities.push(evt.detail.el)\r\n }\r\n el.sceneEl.addEventListener('child-attached', this.childAttachHandler)\r\n\r\n // Update entity list on detach.\r\n this.childDetachHandler = function childDetachHandler (evt) {\r\n if (!evt.detail.el.matches(data.collisionEntities)) { return }\r\n const index = collisionEntities.indexOf(evt.detail.el)\r\n if (index === -1) { return }\r\n collisionEntities.splice(index, 1)\r\n }\r\n el.sceneEl.addEventListener('child-detached', this.childDetachHandler)\r\n },\r\n\r\n onButtonDown: function () {\r\n this.active = true\r\n this.redrawLine = true\r\n },\r\n\r\n /**\r\n * Jump!\r\n */\r\n onButtonUp: (function () {\r\n const newRigLocalPosition = new THREE.Vector3()\r\n const newHandPosition = [new THREE.Vector3(), new THREE.Vector3()] // Left and right\r\n const handPosition = new THREE.Vector3()\r\n\r\n return function (evt) {\r\n if (!this.active) { return }\r\n\r\n // Hide the hit point and the curve\r\n this.active = false\r\n this.hitEntity.setAttribute('visible', false)\r\n this.teleportEntity.setAttribute('visible', false)\r\n\r\n if (!this.hit) {\r\n // Button released but no hit point\r\n return\r\n }\r\n\r\n const rig = this.data.cameraRig || this.el.sceneEl.camera.el\r\n rig.object3D.getWorldPosition(this.rigWorldPosition)\r\n this.newRigWorldPosition.copy(this.hitPoint)\r\n\r\n // Finally update the rigs position\r\n newRigLocalPosition.copy(this.newRigWorldPosition)\r\n if (rig.object3D.parent) {\r\n rig.object3D.parent.worldToLocal(newRigLocalPosition)\r\n }\r\n rig.setAttribute('position', newRigLocalPosition)\r\n\r\n // Also take the headset/camera rotation itself into account\r\n if (this.data.rotateOnTeleport) {\r\n this.teleportOriginQuaternion\r\n .setFromEuler(new THREE.Euler(0, this.teleportOrigin.object3D.rotation.y, 0))\r\n this.teleportOriginQuaternion.invert()\r\n this.teleportOriginQuaternion.multiply(this.hitEntityQuaternion)\r\n // Rotate the rig based on calculated teleport origin rotation\r\n this.cameraRig.object3D.setRotationFromQuaternion(this.teleportOriginQuaternion)\r\n }\r\n\r\n // If a rig was not explicitly declared, look for hands and move them proportionally as well\r\n if (!this.data.cameraRig) {\r\n const hands = document.querySelectorAll('a-entity[tracked-controls]')\r\n for (let i = 0; i < hands.length; i++) {\r\n hands[i].object3D.getWorldPosition(handPosition)\r\n\r\n // diff = rigWorldPosition - handPosition\r\n // newPos = newRigWorldPosition - diff\r\n newHandPosition[i].copy(this.newRigWorldPosition).sub(this.rigWorldPosition).add(handPosition)\r\n hands[i].setAttribute('position', newHandPosition[i])\r\n }\r\n }\r\n\r\n this.el.emit('teleported', this.teleportEventDetail)\r\n }\r\n })(),\r\n\r\n cancel: function () {\r\n this.active = false\r\n this.hitEntity.setAttribute('visible', false)\r\n this.teleportEntity.setAttribute('visible', false)\r\n },\r\n\r\n /**\r\n * Check for raycaster intersection.\r\n *\r\n * @param {number} Line fragment point index.\r\n * @param {number} Last line fragment point index.\r\n * @param {number} Next line fragment point index.\r\n * @returns {boolean} true if there's an intersection.\r\n */\r\n checkMeshCollisions: function (i, last, next) {\r\n // @todo We should add a property to define if the collisionEntity is dynamic or static\r\n // If static we should do the map just once, otherwise we're recreating the array in every\r\n // loop when aiming.\r\n let meshes\r\n if (!this.data.collisionEntities) {\r\n meshes = this.defaultCollisionMeshes\r\n } else {\r\n meshes = this.collisionEntities.map(function (entity) {\r\n return entity.getObject3D('mesh')\r\n }).filter(function (n) { return n })\r\n meshes = meshes.length ? meshes : this.defaultCollisionMeshes\r\n }\r\n\r\n const intersects = this.raycaster.intersectObjects(meshes, true)\r\n if (intersects.length > 0 && !this.hit &&\r\n this.isValidNormalsAngle(intersects[0].face.normal, intersects[0].object)) {\r\n const point = intersects[0].point\r\n\r\n this.line.material.color.set(this.curveHitColor)\r\n this.line.material.opacity = this.data.hitOpacity\r\n this.line.material.transparent = this.data.hitOpacity < 1\r\n this.hitEntity.setAttribute('position', point)\r\n this.hitEntity.setAttribute('visible', true)\r\n\r\n this.hit = true\r\n this.hitPoint.copy(intersects[0].point)\r\n\r\n // If hit, just fill the rest of the points with the hit point and break the loop\r\n for (let j = i; j < this.line.numPoints; j++) {\r\n this.line.setPoint(j, last, this.hitPoint)\r\n }\r\n return true\r\n } else {\r\n this.line.setPoint(i, last, next)\r\n return false\r\n }\r\n },\r\n\r\n isValidNormalsAngle: function (collisionNormal, collisionObject) {\r\n this.collisionObjectNormalMatrix.getNormalMatrix(collisionObject.matrixWorld)\r\n this.collisionWorldNormal.copy(collisionNormal)\r\n .applyMatrix3(this.collisionObjectNormalMatrix).normalize()\r\n const angleNormals = this.referenceNormal.angleTo(this.collisionWorldNormal)\r\n return (THREE.MathUtils.RAD2DEG * angleNormals <= this.data.landingMaxAngle)\r\n },\r\n\r\n // Utils\r\n // Parabolic motion equation, y = p0 + v0*t + 1/2at^2\r\n parabolicCurveScalar: function (p0, v0, a, t) {\r\n return p0 + v0 * t + 0.5 * a * t * t\r\n },\r\n\r\n // Parabolic motion equation applied to 3 dimensions\r\n parabolicCurve: function (p0, v0, a, t, out) {\r\n out.x = this.parabolicCurveScalar(p0.x, v0.x, a.x, t)\r\n out.y = this.parabolicCurveScalar(p0.y, v0.y, a.y, t)\r\n out.z = this.parabolicCurveScalar(p0.z, v0.z, a.z, t)\r\n return out\r\n },\r\n\r\n // To determine how long in terms of t we need to calculate\r\n parabolicCurveMaxRoot: function (p0, v0, a) {\r\n const root = (-v0.y - Math.sqrt(v0.y ** 2 - 4 * (0.5 * a.y) * p0.y)) / (2 * 0.5 * a.y)\r\n return root\r\n },\r\n\r\n createLine: function (data) {\r\n const numPoints = data.type === 'line' ? 2 : data.curveNumberPoints\r\n return new AFRAME.utils.RayCurve(numPoints, data.curveLineWidth)\r\n },\r\n\r\n /**\r\n * Create mesh to represent the area of intersection.\r\n * Default to a combination of torus and cylinder.\r\n */\r\n createHitEntity: function (data) {\r\n // Parent.\r\n const hitEntity = document.createElement('a-entity')\r\n hitEntity.className = 'hitEntity'\r\n\r\n // Torus.\r\n const torus = document.createElement('a-entity')\r\n torus.setAttribute('geometry', {\r\n primitive: 'torus',\r\n radius: data.hitCylinderRadius,\r\n radiusTubular: 0.01\r\n })\r\n torus.setAttribute('rotation', { x: 90, y: 0, z: 0 })\r\n torus.setAttribute('material', {\r\n shader: 'flat',\r\n color: data.hitCylinderColor,\r\n side: 'double',\r\n depthTest: false\r\n })\r\n hitEntity.appendChild(torus)\r\n\r\n // Cylinder.\r\n const cylinder = document.createElement('a-entity')\r\n cylinder.setAttribute('position', { x: 0, y: data.hitCylinderHeight / 2, z: 0 })\r\n cylinder.setAttribute('geometry', {\r\n primitive: 'cylinder',\r\n segmentsHeight: 1,\r\n radius: data.hitCylinderRadius,\r\n height: data.hitCylinderHeight,\r\n openEnded: true\r\n })\r\n cylinder.setAttribute('material', {\r\n shader: 'flat',\r\n color: data.hitCylinderColor,\r\n opacity: 0.5,\r\n side: 'double',\r\n src: this.cylinderTexture,\r\n transparent: true,\r\n depthTest: false\r\n })\r\n hitEntity.appendChild(cylinder)\r\n\r\n const pointer = document.createElement('a-entity')\r\n pointer.setAttribute('position', { x: 0, y: 0.05, z: data.hitCylinderRadius * -1.5 })\r\n pointer.setAttribute('rotation', { x: 90, y: 180, z: 0 })\r\n pointer.setAttribute('geometry', {\r\n primitive: 'prism',\r\n height: 0.2,\r\n width: 0.2,\r\n depth: 0.05\r\n })\r\n pointer.setAttribute('material', {\r\n shader: 'flat',\r\n color: data.hitCylinderColor,\r\n side: 'double',\r\n transparent: true,\r\n opacity: 0.6,\r\n depthTest: false\r\n })\r\n hitEntity.appendChild(pointer)\r\n\r\n return hitEntity\r\n },\r\n createDefaultPlane: function (size) {\r\n const geometry = new THREE.PlaneGeometry(100, 100)\r\n geometry.rotateX(-Math.PI / 2)\r\n const material = new THREE.MeshBasicMaterial({ color: 0xffff00 })\r\n return new THREE.Mesh(geometry, material)\r\n },\r\n cylinderTexture: 'url(data:image/png;base64,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)'\r\n})\r\n\r\nAFRAME.utils.RayCurve = function (numPoints, width) {\r\n this.geometry = new THREE.BufferGeometry()\r\n this.vertices = new Float32Array(numPoints * 3 * 6) // 6 vertices (2 triangles) * 3 dimensions\r\n this.uvs = new Float32Array(numPoints * 2 * 6) // 2 uvs per vertex\r\n this.width = width\r\n\r\n this.geometry.setAttribute('position', new THREE.BufferAttribute(this.vertices, 3).setUsage(THREE.DynamicDrawUsage))\r\n\r\n this.material = new THREE.MeshBasicMaterial({\r\n side: THREE.DoubleSide,\r\n color: 0xff0000\r\n })\r\n\r\n this.mesh = new THREE.Mesh(this.geometry, this.material)\r\n\r\n this.mesh.frustumCulled = false\r\n this.mesh.vertices = this.vertices\r\n\r\n this.direction = new THREE.Vector3()\r\n this.numPoints = numPoints\r\n}\r\n\r\nAFRAME.utils.RayCurve.prototype = {\r\n setDirection: function (direction) {\r\n const UP = new THREE.Vector3(0, 1, 0)\r\n this.direction\r\n .copy(direction)\r\n .cross(UP)\r\n .normalize()\r\n .multiplyScalar(this.width / 2)\r\n },\r\n\r\n setWidth: function (width) {\r\n this.width = width\r\n },\r\n\r\n setPoint: (function () {\r\n const posA = new THREE.Vector3()\r\n const posB = new THREE.Vector3()\r\n const posC = new THREE.Vector3()\r\n const posD = new THREE.Vector3()\r\n\r\n return function (i, last, next) {\r\n posA.copy(last).add(this.direction)\r\n posB.copy(last).sub(this.direction)\r\n\r\n posC.copy(next).add(this.direction)\r\n posD.copy(next).sub(this.direction)\r\n\r\n let idx = 6 * 3 * i // 6 vertices per point\r\n\r\n this.vertices[idx++] = posA.x\r\n this.vertices[idx++] = posA.y\r\n this.vertices[idx++] = posA.z\r\n\r\n this.vertices[idx++] = posB.x\r\n this.vertices[idx++] = posB.y\r\n this.vertices[idx++] = posB.z\r\n\r\n this.vertices[idx++] = posC.x\r\n this.vertices[idx++] = posC.y\r\n this.vertices[idx++] = posC.z\r\n\r\n this.vertices[idx++] = posC.x\r\n this.vertices[idx++] = posC.y\r\n this.vertices[idx++] = posC.z\r\n\r\n this.vertices[idx++] = posB.x\r\n this.vertices[idx++] = posB.y\r\n this.vertices[idx++] = posB.z\r\n\r\n this.vertices[idx++] = posD.x\r\n this.vertices[idx++] = posD.y\r\n this.vertices[idx++] = posD.z\r\n\r\n this.geometry.attributes.position.needsUpdate = true\r\n }\r\n })()\r\n}\r\n"],"names":[],"sourceRoot":""} \ No newline at end of file diff --git a/package.json b/package.json index 6157bef..3d27aaf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "aframe-blink-controls", - "version": "0.4.1", + "version": "0.4.3", "description": "Blink controls component for A-Frame.", "main": "src/index.js", "unpkg": "dist/aframe-blink-controls.min.js",