aframe-teleport-controls.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609
  1. /******/ (function(modules) { // webpackBootstrap
  2. /******/ // The module cache
  3. /******/ var installedModules = {};
  4. /******/ // The require function
  5. /******/ function __webpack_require__(moduleId) {
  6. /******/ // Check if module is in cache
  7. /******/ if(installedModules[moduleId])
  8. /******/ return installedModules[moduleId].exports;
  9. /******/ // Create a new module (and put it into the cache)
  10. /******/ var module = installedModules[moduleId] = {
  11. /******/ exports: {},
  12. /******/ id: moduleId,
  13. /******/ loaded: false
  14. /******/ };
  15. /******/ // Execute the module function
  16. /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
  17. /******/ // Flag the module as loaded
  18. /******/ module.loaded = true;
  19. /******/ // Return the exports of the module
  20. /******/ return module.exports;
  21. /******/ }
  22. /******/ // expose the modules object (__webpack_modules__)
  23. /******/ __webpack_require__.m = modules;
  24. /******/ // expose the module cache
  25. /******/ __webpack_require__.c = installedModules;
  26. /******/ // __webpack_public_path__
  27. /******/ __webpack_require__.p = "";
  28. /******/ // Load entry module and return exports
  29. /******/ return __webpack_require__(0);
  30. /******/ })
  31. /************************************************************************/
  32. /******/ ([
  33. /* 0 */
  34. /***/ (function(module, exports, __webpack_require__) {
  35. /* global THREE, AFRAME, Element */
  36. var cylinderTexture = __webpack_require__(1);
  37. var parabolicCurve = __webpack_require__(2);
  38. var RayCurve = __webpack_require__(3);
  39. if (typeof AFRAME === 'undefined') {
  40. throw new Error('Component attempted to register before AFRAME was available.');
  41. }
  42. if (!Element.prototype.matches) {
  43. Element.prototype.matches =
  44. Element.prototype.matchesSelector ||
  45. Element.prototype.mozMatchesSelector ||
  46. Element.prototype.msMatchesSelector ||
  47. Element.prototype.oMatchesSelector ||
  48. Element.prototype.webkitMatchesSelector ||
  49. function (s) {
  50. var matches = (this.document || this.ownerDocument).querySelectorAll(s);
  51. var i = matches.length;
  52. while (--i >= 0 && matches.item(i) !== this) { /* no-op */ }
  53. return i > -1;
  54. };
  55. }
  56. AFRAME.registerComponent('teleport-controls', {
  57. schema: {
  58. type: {default: 'parabolic', oneOf: ['parabolic', 'line']},
  59. button: {default: 'trackpad', oneOf: ['trackpad', 'trigger', 'grip', 'menu']},
  60. startEvents: {type: 'array'},
  61. endEvents: {type: 'array'},
  62. collisionEntities: {default: ''},
  63. hitEntity: {type: 'selector'},
  64. cameraRig: {type: 'selector'},
  65. teleportOrigin: {type: 'selector'},
  66. hitCylinderColor: {type: 'color', default: '#99ff99'},
  67. hitCylinderRadius: {default: 0.25, min: 0},
  68. hitCylinderHeight: {default: 0.3, min: 0},
  69. interval: {default: 0},
  70. maxLength: {default: 10, min: 0, if: {type: ['line']}},
  71. curveNumberPoints: {default: 30, min: 2, if: {type: ['parabolic']}},
  72. curveLineWidth: {default: 0.025},
  73. curveHitColor: {type: 'color', default: '#99ff99'},
  74. curveMissColor: {type: 'color', default: '#ff0000'},
  75. curveShootingSpeed: {default: 5, min: 0, if: {type: ['parabolic']}},
  76. defaultPlaneSize: { default: 100 },
  77. landingNormal: {type: 'vec3', default: '0 1 0'},
  78. landingMaxAngle: {default: '45', min: 0, max: 360},
  79. drawIncrementally: {default: false},
  80. incrementalDrawMs: {default: 700},
  81. missOpacity: {default: 1.0},
  82. hitOpacity: {default: 1.0}
  83. },
  84. init: function () {
  85. var data = this.data;
  86. var el = this.el;
  87. var teleportEntity;
  88. var i;
  89. this.active = false;
  90. this.obj = el.object3D;
  91. this.hitPoint = new THREE.Vector3();
  92. this.rigWorldPosition = new THREE.Vector3();
  93. this.newRigWorldPosition = new THREE.Vector3();
  94. this.teleportEventDetail = {
  95. oldPosition: this.rigWorldPosition,
  96. newPosition: this.newRigWorldPosition,
  97. hitPoint: this.hitPoint
  98. };
  99. this.hit = false;
  100. this.prevCheckTime = undefined;
  101. this.prevHitHeight = 0;
  102. this.referenceNormal = new THREE.Vector3();
  103. this.curveMissColor = new THREE.Color();
  104. this.curveHitColor = new THREE.Color();
  105. this.raycaster = new THREE.Raycaster();
  106. this.defaultPlane = createDefaultPlane(this.data.defaultPlaneSize);
  107. this.defaultCollisionMeshes = [this.defaultPlane];
  108. teleportEntity = this.teleportEntity = document.createElement('a-entity');
  109. teleportEntity.classList.add('teleportRay');
  110. teleportEntity.setAttribute('visible', false);
  111. el.sceneEl.appendChild(this.teleportEntity);
  112. this.onButtonDown = this.onButtonDown.bind(this);
  113. this.onButtonUp = this.onButtonUp.bind(this);
  114. if (this.data.startEvents.length && this.data.endEvents.length) {
  115. for (i = 0; i < this.data.startEvents.length; i++) {
  116. el.addEventListener(this.data.startEvents[i], this.onButtonDown);
  117. }
  118. for (i = 0; i < this.data.endEvents.length; i++) {
  119. el.addEventListener(this.data.endEvents[i], this.onButtonUp);
  120. }
  121. } else {
  122. el.addEventListener(data.button + 'down', this.onButtonDown);
  123. el.addEventListener(data.button + 'up', this.onButtonUp);
  124. }
  125. this.queryCollisionEntities();
  126. },
  127. update: function (oldData) {
  128. var data = this.data;
  129. var diff = AFRAME.utils.diff(data, oldData);
  130. // Update normal.
  131. this.referenceNormal.copy(data.landingNormal);
  132. // Update colors.
  133. this.curveMissColor.set(data.curveMissColor);
  134. this.curveHitColor.set(data.curveHitColor);
  135. // Create or update line mesh.
  136. if (!this.line ||
  137. 'curveLineWidth' in diff || 'curveNumberPoints' in diff || 'type' in diff) {
  138. this.line = createLine(data);
  139. this.line.material.opacity = this.data.hitOpacity;
  140. this.line.material.transparent = this.data.hitOpacity < 1;
  141. this.numActivePoints = data.curveNumberPoints;
  142. this.teleportEntity.setObject3D('mesh', this.line.mesh);
  143. }
  144. // Create or update hit entity.
  145. if (data.hitEntity) {
  146. this.hitEntity = data.hitEntity;
  147. } else if (!this.hitEntity || 'hitCylinderColor' in diff || 'hitCylinderHeight' in diff ||
  148. 'hitCylinderRadius' in diff) {
  149. // Remove previous entity, create new entity (could be more performant).
  150. if (this.hitEntity) { this.hitEntity.parentNode.removeChild(this.hitEntity); }
  151. this.hitEntity = createHitEntity(data);
  152. this.el.sceneEl.appendChild(this.hitEntity);
  153. }
  154. this.hitEntity.setAttribute('visible', false);
  155. if ('collisionEntities' in diff) { this.queryCollisionEntities(); }
  156. },
  157. remove: function () {
  158. var el = this.el;
  159. var hitEntity = this.hitEntity;
  160. var teleportEntity = this.teleportEntity;
  161. if (hitEntity) { hitEntity.parentNode.removeChild(hitEntity); }
  162. if (teleportEntity) { teleportEntity.parentNode.removeChild(teleportEntity); }
  163. el.sceneEl.removeEventListener('child-attached', this.childAttachHandler);
  164. el.sceneEl.removeEventListener('child-detached', this.childDetachHandler);
  165. },
  166. tick: (function () {
  167. var p0 = new THREE.Vector3();
  168. var v0 = new THREE.Vector3();
  169. var g = -9.8;
  170. var a = new THREE.Vector3(0, g, 0);
  171. var next = new THREE.Vector3();
  172. var last = new THREE.Vector3();
  173. var quaternion = new THREE.Quaternion();
  174. var translation = new THREE.Vector3();
  175. var scale = new THREE.Vector3();
  176. var shootAngle = new THREE.Vector3();
  177. var lastNext = new THREE.Vector3();
  178. var auxDirection = new THREE.Vector3();
  179. var timeSinceDrawStart = 0;
  180. return function (time, delta) {
  181. if (!this.active) { return; }
  182. if (this.data.drawIncrementally && this.redrawLine){
  183. this.redrawLine = false;
  184. timeSinceDrawStart = 0;
  185. }
  186. timeSinceDrawStart += delta;
  187. this.numActivePoints = this.data.curveNumberPoints*timeSinceDrawStart/this.data.incrementalDrawMs;
  188. if (this.numActivePoints > this.data.curveNumberPoints){
  189. this.numActivePoints = this.data.curveNumberPoints;
  190. }
  191. // Only check for intersection if interval time has passed.
  192. if (this.prevCheckTime && (time - this.prevCheckTime < this.data.interval)) { return; }
  193. // Update check time.
  194. this.prevCheckTime = time;
  195. var matrixWorld = this.obj.matrixWorld;
  196. matrixWorld.decompose(translation, quaternion, scale);
  197. var direction = shootAngle.set(0, 0, -1)
  198. .applyQuaternion(quaternion).normalize();
  199. this.line.setDirection(auxDirection.copy(direction));
  200. this.obj.getWorldPosition(p0);
  201. last.copy(p0);
  202. // Set default status as non-hit
  203. this.teleportEntity.setAttribute('visible', true);
  204. this.line.material.color.set(this.curveMissColor);
  205. this.line.material.opacity = this.data.missOpacity;
  206. this.line.material.transparent = this.data.missOpacity < 1;
  207. this.hitEntity.setAttribute('visible', false);
  208. this.hit = false;
  209. if (this.data.type === 'parabolic') {
  210. v0.copy(direction).multiplyScalar(this.data.curveShootingSpeed);
  211. this.lastDrawnIndex = 0;
  212. const numPoints = this.data.drawIncrementally ? this.numActivePoints : this.line.numPoints;
  213. for (var i = 0; i < numPoints+1; i++) {
  214. var t;
  215. if (i == Math.floor(numPoints+1)){
  216. t = numPoints / (this.line.numPoints - 1);
  217. }
  218. else {
  219. t = i / (this.line.numPoints - 1);
  220. }
  221. parabolicCurve(p0, v0, a, t, next);
  222. // Update the raycaster with the length of the current segment last->next
  223. var dirLastNext = lastNext.copy(next).sub(last).normalize();
  224. this.raycaster.far = dirLastNext.length();
  225. this.raycaster.set(last, dirLastNext);
  226. this.lastDrawnPoint = next;
  227. this.lastDrawnIndex = i;
  228. if (this.checkMeshCollisions(i, next)) { break; }
  229. last.copy(next);
  230. }
  231. for (var j = this.lastDrawnIndex+1; j < this.line.numPoints; j++) {
  232. this.line.setPoint(j, this.lastDrawnPoint);
  233. }
  234. } else if (this.data.type === 'line') {
  235. next.copy(last).add(auxDirection.copy(direction).multiplyScalar(this.data.maxLength));
  236. this.raycaster.far = this.data.maxLength;
  237. this.raycaster.set(p0, direction);
  238. this.line.setPoint(0, p0);
  239. this.checkMeshCollisions(1, next);
  240. }
  241. };
  242. })(),
  243. /**
  244. * Run `querySelectorAll` for `collisionEntities` and maintain it with `child-attached`
  245. * and `child-detached` events.
  246. */
  247. queryCollisionEntities: function () {
  248. var collisionEntities;
  249. var data = this.data;
  250. var el = this.el;
  251. if (!data.collisionEntities) {
  252. this.collisionEntities = [];
  253. return;
  254. }
  255. collisionEntities = [].slice.call(el.sceneEl.querySelectorAll(data.collisionEntities));
  256. this.collisionEntities = collisionEntities;
  257. // Update entity list on attach.
  258. this.childAttachHandler = function childAttachHandler (evt) {
  259. if (!evt.detail.el.matches(data.collisionEntities)) { return; }
  260. collisionEntities.push(evt.detail.el);
  261. };
  262. el.sceneEl.addEventListener('child-attached', this.childAttachHandler);
  263. // Update entity list on detach.
  264. this.childDetachHandler = function childDetachHandler (evt) {
  265. var index;
  266. if (!evt.detail.el.matches(data.collisionEntities)) { return; }
  267. index = collisionEntities.indexOf(evt.detail.el);
  268. if (index === -1) { return; }
  269. collisionEntities.splice(index, 1);
  270. };
  271. el.sceneEl.addEventListener('child-detached', this.childDetachHandler);
  272. },
  273. onButtonDown: function () {
  274. this.active = true;
  275. this.redrawLine = true;
  276. },
  277. /**
  278. * Jump!
  279. */
  280. onButtonUp: (function () {
  281. const teleportOriginWorldPosition = new THREE.Vector3();
  282. const newRigLocalPosition = new THREE.Vector3();
  283. const newHandPosition = [new THREE.Vector3(), new THREE.Vector3()]; // Left and right
  284. const handPosition = new THREE.Vector3();
  285. return function (evt) {
  286. if (!this.active) { return; }
  287. // Hide the hit point and the curve
  288. this.active = false;
  289. this.hitEntity.setAttribute('visible', false);
  290. this.teleportEntity.setAttribute('visible', false);
  291. if (!this.hit) {
  292. // Button released but not hit point
  293. return;
  294. }
  295. const rig = this.data.cameraRig || this.el.sceneEl.camera.el;
  296. rig.object3D.getWorldPosition(this.rigWorldPosition);
  297. this.newRigWorldPosition.copy(this.hitPoint);
  298. // If a teleportOrigin exists, offset the rig such that the teleportOrigin is above the hitPoint
  299. const teleportOrigin = this.data.teleportOrigin;
  300. if (teleportOrigin) {
  301. teleportOrigin.object3D.getWorldPosition(teleportOriginWorldPosition);
  302. this.newRigWorldPosition.sub(teleportOriginWorldPosition).add(this.rigWorldPosition);
  303. }
  304. // Always keep the rig at the same offset off the ground after teleporting
  305. this.newRigWorldPosition.y = this.rigWorldPosition.y + this.hitPoint.y - this.prevHitHeight;
  306. this.prevHitHeight = this.hitPoint.y;
  307. // Finally update the rigs position
  308. newRigLocalPosition.copy(this.newRigWorldPosition);
  309. if (rig.object3D.parent) {
  310. rig.object3D.parent.worldToLocal(newRigLocalPosition);
  311. }
  312. rig.setAttribute('position', newRigLocalPosition);
  313. // If a rig was not explicitly declared, look for hands and mvoe them proportionally as well
  314. if (!this.data.cameraRig) {
  315. var hands = document.querySelectorAll('a-entity[tracked-controls]');
  316. for (var i = 0; i < hands.length; i++) {
  317. hands[i].object3D.getWorldPosition(handPosition);
  318. // diff = rigWorldPosition - handPosition
  319. // newPos = newRigWorldPosition - diff
  320. newHandPosition[i].copy(this.newRigWorldPosition).sub(this.rigWorldPosition).add(handPosition);
  321. hands[i].setAttribute('position', newHandPosition[i]);
  322. }
  323. }
  324. this.el.emit('teleported', this.teleportEventDetail);
  325. };
  326. })(),
  327. /**
  328. * Check for raycaster intersection.
  329. *
  330. * @param {number} Line fragment point index.
  331. * @param {number} Next line fragment point index.
  332. * @returns {boolean} true if there's an intersection.
  333. */
  334. checkMeshCollisions: function (i, next) {
  335. // @todo We should add a property to define if the collisionEntity is dynamic or static
  336. // If static we should do the map just once, otherwise we're recreating the array in every
  337. // loop when aiming.
  338. var meshes;
  339. if (!this.data.collisionEntities) {
  340. meshes = this.defaultCollisionMeshes;
  341. } else {
  342. meshes = this.collisionEntities.map(function (entity) {
  343. return entity.getObject3D('mesh');
  344. }).filter(function (n) { return n; });
  345. meshes = meshes.length ? meshes : this.defaultCollisionMeshes;
  346. }
  347. var intersects = this.raycaster.intersectObjects(meshes, true);
  348. if (intersects.length > 0 && !this.hit &&
  349. this.isValidNormalsAngle(intersects[0].face.normal)) {
  350. var point = intersects[0].point;
  351. this.line.material.color.set(this.curveHitColor);
  352. this.line.material.opacity = this.data.hitOpacity;
  353. this.line.material.transparent= this.data.hitOpacity < 1;
  354. this.hitEntity.setAttribute('position', point);
  355. this.hitEntity.setAttribute('visible', true);
  356. this.hit = true;
  357. this.hitPoint.copy(intersects[0].point);
  358. // If hit, just fill the rest of the points with the hit point and break the loop
  359. for (var j = i; j < this.line.numPoints; j++) {
  360. this.line.setPoint(j, this.hitPoint);
  361. }
  362. return true;
  363. } else {
  364. this.line.setPoint(i, next);
  365. return false;
  366. }
  367. },
  368. isValidNormalsAngle: function (collisionNormal) {
  369. var angleNormals = this.referenceNormal.angleTo(collisionNormal);
  370. return (THREE.Math.RAD2DEG * angleNormals <= this.data.landingMaxAngle);
  371. },
  372. });
  373. function createLine (data) {
  374. var numPoints = data.type === 'line' ? 2 : data.curveNumberPoints;
  375. return new RayCurve(numPoints, data.curveLineWidth);
  376. }
  377. /**
  378. * Create mesh to represent the area of intersection.
  379. * Default to a combination of torus and cylinder.
  380. */
  381. function createHitEntity (data) {
  382. var cylinder;
  383. var hitEntity;
  384. var torus;
  385. // Parent.
  386. hitEntity = document.createElement('a-entity');
  387. hitEntity.className = 'hitEntity';
  388. // Torus.
  389. torus = document.createElement('a-entity');
  390. torus.setAttribute('geometry', {
  391. primitive: 'torus',
  392. radius: data.hitCylinderRadius,
  393. radiusTubular: 0.01
  394. });
  395. torus.setAttribute('rotation', {x: 90, y: 0, z: 0});
  396. torus.setAttribute('material', {
  397. shader: 'flat',
  398. color: data.hitCylinderColor,
  399. side: 'double',
  400. depthTest: false
  401. });
  402. hitEntity.appendChild(torus);
  403. // Cylinder.
  404. cylinder = document.createElement('a-entity');
  405. cylinder.setAttribute('position', {x: 0, y: data.hitCylinderHeight / 2, z: 0});
  406. cylinder.setAttribute('geometry', {
  407. primitive: 'cylinder',
  408. segmentsHeight: 1,
  409. radius: data.hitCylinderRadius,
  410. height: data.hitCylinderHeight,
  411. openEnded: true
  412. });
  413. cylinder.setAttribute('material', {
  414. shader: 'flat',
  415. color: data.hitCylinderColor,
  416. side: 'double',
  417. src: cylinderTexture,
  418. transparent: true,
  419. depthTest: false
  420. });
  421. hitEntity.appendChild(cylinder);
  422. return hitEntity;
  423. }
  424. function createDefaultPlane (size) {
  425. var geometry;
  426. var material;
  427. geometry = new THREE.PlaneBufferGeometry(100, 100);
  428. geometry.rotateX(-Math.PI / 2);
  429. material = new THREE.MeshBasicMaterial({color: 0xffff00});
  430. return new THREE.Mesh(geometry, material);
  431. }
  432. /***/ }),
  433. /* 1 */
  434. /***/ (function(module, exports) {
  435. module.exports = 'url(data:image/png;base64,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)';
  436. /***/ }),
  437. /* 2 */
  438. /***/ (function(module, exports) {
  439. /* global THREE */
  440. // Parabolic motion equation, y = p0 + v0*t + 1/2at^2
  441. function parabolicCurveScalar (p0, v0, a, t) {
  442. return p0 + v0 * t + 0.5 * a * t * t;
  443. }
  444. // Parabolic motion equation applied to 3 dimensions
  445. function parabolicCurve (p0, v0, a, t, out) {
  446. out.x = parabolicCurveScalar(p0.x, v0.x, a.x, t);
  447. out.y = parabolicCurveScalar(p0.y, v0.y, a.y, t);
  448. out.z = parabolicCurveScalar(p0.z, v0.z, a.z, t);
  449. return out;
  450. }
  451. module.exports = parabolicCurve;
  452. /***/ }),
  453. /* 3 */
  454. /***/ (function(module, exports) {
  455. /* global THREE */
  456. var RayCurve = function (numPoints, width) {
  457. this.geometry = new THREE.BufferGeometry();
  458. this.vertices = new Float32Array(numPoints * 3 * 2);
  459. this.uvs = new Float32Array(numPoints * 2 * 2);
  460. this.width = width;
  461. this.geometry.setAttribute('position', new THREE.BufferAttribute(this.vertices, 3).setUsage(THREE.DynamicDrawUsage));
  462. this.material = new THREE.MeshBasicMaterial({
  463. side: THREE.DoubleSide,
  464. color: 0xff0000
  465. });
  466. this.mesh = new THREE.Mesh(this.geometry, this.material);
  467. THREE.BufferGeometryUtils.toTrianglesDrawMode(this.mesh.geometry, THREE.TriangleStripDrawMode);
  468. //this.mesh.drawMode = THREE.TriangleStripDrawMode;
  469. this.mesh.frustumCulled = false;
  470. this.mesh.vertices = this.vertices;
  471. this.direction = new THREE.Vector3();
  472. this.numPoints = numPoints;
  473. };
  474. RayCurve.prototype = {
  475. setDirection: function (direction) {
  476. var UP = new THREE.Vector3(0, 1, 0);
  477. this.direction
  478. .copy(direction)
  479. .cross(UP)
  480. .normalize()
  481. .multiplyScalar(this.width / 2);
  482. },
  483. setWidth: function (width) {
  484. this.width = width;
  485. },
  486. setPoint: (function () {
  487. var posA = new THREE.Vector3();
  488. var posB = new THREE.Vector3();
  489. return function (i, point) {
  490. posA.copy(point).add(this.direction);
  491. posB.copy(point).sub(this.direction);
  492. var idx = 2 * 3 * i;
  493. this.vertices[idx++] = posA.x;
  494. this.vertices[idx++] = posA.y;
  495. this.vertices[idx++] = posA.z;
  496. this.vertices[idx++] = posB.x;
  497. this.vertices[idx++] = posB.y;
  498. this.vertices[idx++] = posB.z;
  499. this.geometry.attributes.position.needsUpdate = true;
  500. };
  501. })()
  502. };
  503. module.exports = RayCurve;
  504. /***/ })
  505. /******/ ]);