Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

aframe-with-martins.js 7.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. /**
  2. * @file MARTINS.js & AFRAME glue code
  3. * @version 1.0.2
  4. * @author Alexandre Martins (https://github.com/alemart)
  5. * @license AGPL-3.0
  6. */
  7. /* Usage of the indicated versions is encouraged */
  8. __THIS_GLUE_CODE_HAS_BEEN_TESTED_WITH__({
  9. 'MARTINS.js': { version: '0.1.2' },
  10. 'AFRAME': { version: '1.3.0' }
  11. });
  12. /**
  13. * Connect MARTINS.js to AFRAME
  14. * @param {(canvas: HTMLCanvasElement) => Promise<Session> | SpeedyPromise<Session>} startARSession
  15. */
  16. function linkMartinsToAFRAME(startARSession)
  17. {
  18. AFRAME.registerSystem('ar-system', {
  19. init()
  20. {
  21. this.state = {
  22. isTracking: false,
  23. frame: null,
  24. referenceImage: null,
  25. projectionMatrix: null,
  26. viewMatrixInverse: null,
  27. modelMatrix: null,
  28. };
  29. this.session = null;
  30. return startARSession(this.el.canvas).then(session => {
  31. this.session = session;
  32. session.addEventListener('end', event => {
  33. this.state.isTracking = false;
  34. });
  35. session.viewport.addEventListener('resize', event => {
  36. this.updateRenderer(session.viewport.virtualSize);
  37. });
  38. if(session.viewport.canvas !== this.el.canvas) {
  39. session.end();
  40. throw new Error('Invalid AFRAME <canvas>');
  41. }
  42. const animate = (time, frame) => {
  43. this.updateState(frame);
  44. this.renderVirtualScene();
  45. session.requestAnimationFrame(animate);
  46. };
  47. session.requestAnimationFrame(animate);
  48. }).catch(error => {
  49. console.error(error);
  50. alert(error.message);
  51. });
  52. },
  53. updateState(frame)
  54. {
  55. const wasTracking = this.state.isTracking;
  56. this.state.frame = frame;
  57. this.state.isTracking = false;
  58. this.state.referenceImage = null;
  59. for(const result of frame.results) {
  60. if(result.tracker.type == 'image-tracker') {
  61. if(result.trackables.length > 0) {
  62. const trackable = result.trackables[0];
  63. this.state.projectionMatrix = result.viewer.view.projectionMatrix;
  64. this.state.viewMatrixInverse = result.viewer.pose.transform.matrix;
  65. this.state.modelMatrix = trackable.pose.transform.matrix;
  66. this.state.referenceImage = trackable.referenceImage;
  67. this.state.isTracking = true;
  68. }
  69. }
  70. }
  71. if(this.state.isTracking && !wasTracking)
  72. this.updateRenderer(frame.session.viewport.virtualSize);
  73. },
  74. updateRenderer(size)
  75. {
  76. const renderer = this.el.renderer;
  77. const resize = () => {
  78. renderer.setPixelRatio(1.0);
  79. renderer.setSize(size.width, size.height, false);
  80. };
  81. resize();
  82. setTimeout(resize, 200); // internals of AFRAME (a-scene.js)
  83. },
  84. renderVirtualScene()
  85. {
  86. const scene = this.el;
  87. if(!scene.camera || !scene.object3D)
  88. return;
  89. scene.delta = scene.clock.getDelta() * 1000;
  90. scene.time = scene.clock.elapsedTime * 1000;
  91. if(scene.isPlaying)
  92. scene.tick(scene.time, scene.delta);
  93. scene.object3D.background = null;
  94. scene.renderer.render(scene.object3D, scene.camera);
  95. scene.renderer.setAnimationLoop(null);
  96. },
  97. });
  98. AFRAME.registerComponent('ar-root', {
  99. schema: {
  100. 'image-target': { type: 'string', default: '' },
  101. },
  102. init()
  103. {
  104. this.arSystem = this.el.sceneEl.systems['ar-system'];
  105. this.el.object3D.matrixAutoUpdate = false;
  106. this.el.object3D.visible = false;
  107. },
  108. remove()
  109. {
  110. const session = this.arSystem.session;
  111. session.end();
  112. },
  113. tick()
  114. {
  115. const ANY = '', target = this.data['image-target'];
  116. const state = this.arSystem.state;
  117. if(state.isTracking && (target === ANY || target === state.referenceImage.name)) {
  118. this.alignVirtualScene(state.modelMatrix);
  119. this.el.object3D.visible = true;
  120. }
  121. else
  122. this.el.object3D.visible = false;
  123. },
  124. alignVirtualScene(modelMatrix)
  125. {
  126. const arRoot = this.el.object3D;
  127. arRoot.matrix.fromArray(modelMatrix.read());
  128. arRoot.updateMatrixWorld(true);
  129. }
  130. });
  131. AFRAME.registerPrimitive('ar-root', AFRAME.utils.extendDeep({}, AFRAME.primitives.getMeshMixin(), {
  132. defaultComponents: {
  133. 'ar-root': {}
  134. },
  135. mappings: {
  136. 'image-target': 'ar-root.image-target'
  137. }
  138. }));
  139. AFRAME.registerComponent('ar-camera', {
  140. init()
  141. {
  142. this.arSystem = this.el.sceneEl.systems['ar-system'];
  143. this.arCamera = this.el.getObject3D('camera');
  144. this.arCamera.matrixAutoUpdate = false;
  145. this.el.setAttribute('camera', { active: true });
  146. this.el.setAttribute('wasd-controls', { enabled: false });
  147. this.el.setAttribute('look-controls', { enabled: false });
  148. this.el.setAttribute('position', { x: 0, y: 0, z: 0 }); // AFRAME sets y = 1.6m for VR
  149. },
  150. tick()
  151. {
  152. const state = this.arSystem.state;
  153. if(state.isTracking)
  154. this.updateCamera(state.projectionMatrix, state.viewMatrixInverse);
  155. },
  156. updateCamera(projectionMatrix, viewMatrixInverse)
  157. {
  158. const arCamera = this.arCamera;
  159. arCamera.projectionMatrix.fromArray(projectionMatrix.read());
  160. arCamera.projectionMatrixInverse.copy(arCamera.projectionMatrix).invert();
  161. arCamera.matrix.fromArray(viewMatrixInverse.read());
  162. arCamera.updateMatrixWorld(true);
  163. }
  164. });
  165. /*
  166. // AFRAME won't catch this in setupInitialCamera()
  167. AFRAME.registerPrimitive('ar-camera', AFRAME.utils.extendDeep({}, AFRAME.primitives.getMeshMixin(), {
  168. defaultComponents: {
  169. 'ar-camera': {}
  170. }
  171. }));
  172. */
  173. AFRAME.registerComponent('ar-scene', {
  174. init()
  175. {
  176. this.el.setAttribute('vr-mode-ui', { enabled: false });
  177. this.el.setAttribute('embedded', true);
  178. this.el.setAttribute('renderer', { alpha: true });
  179. }
  180. });
  181. };
  182. // Start automatically
  183. if(typeof startARSession === 'function')
  184. linkMartinsToAFRAME(startARSession);
  185. else
  186. alert('Missing startARSession');
  187. /**
  188. * Version check
  189. * @param {object} json
  190. */
  191. function __THIS_GLUE_CODE_HAS_BEEN_TESTED_WITH__(json)
  192. {
  193. try { Martins, AFRAME;
  194. const versionOf = { 'MARTINS.js': Martins.version.replace(/-.*$/, ''), 'AFRAME': AFRAME.version };
  195. const check = (x,v,w) => v !== w ? console.warn(`\n\n\nWARNING\n\nThis glue code has been tested with ${x} version ${v}. The version in use is ${w}. Usage of ${x} version ${v} is recommended instead.\n\n\n`) : void 0;
  196. for(const [x, expected] of Object.entries(json)) check(x, expected.version, versionOf[x]);
  197. } catch(e) { alert(e.message); }
  198. }