You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

demo.js 5.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. /**
  2. * @file MARTINS.js WebAR demo with three.js and touch interaction
  3. * @author Alexandre Martins (https://github.com/alemart)
  4. * @license LGPL-3.0-or-later
  5. */
  6. window.addEventListener('load', () => {
  7. const my = { };
  8. async function initialize(ar)
  9. {
  10. // add lights
  11. const ambientLight = new THREE.AmbientLight(0x808080);
  12. const directionalLight = new THREE.DirectionalLight(0xffffff, 0.75);
  13. directionalLight.position.set(0, 0, -1);
  14. ar.scene.add(ambientLight);
  15. ar.scene.add(directionalLight);
  16. // create cubes
  17. my.cubes = [
  18. createCube(ar, 1.0, -0.5, 0x00ff00),
  19. createCube(ar, -1.0, -0.5, 0xffff00),
  20. createCube(ar, 0.0, -0.5, 0x0077ff),
  21. ];
  22. // create text
  23. my.text = await createText(ar, 'Tap on any cube', 0x88ffee);
  24. my.text.position.set(-1.5, 0.25, 0.5);
  25. // setup interactivity
  26. my.pointer = createPointer(ar);
  27. my.raycaster = new THREE.Raycaster();
  28. }
  29. function createCube(ar, x, y, color, length = 0.7)
  30. {
  31. const geometry = new THREE.BoxGeometry(length, length, length);
  32. const material = new THREE.MeshPhongMaterial({ color });
  33. const cube = new THREE.Mesh(geometry, material);
  34. cube.position.x = x;
  35. cube.position.y = y;
  36. cube.position.z = length / 2;
  37. cube.userData.color = color;
  38. material.opacity = 1.0;
  39. material.transparent = true;
  40. ar.root.add(cube);
  41. return cube;
  42. }
  43. async function createText(ar, text, color = 0xffffff)
  44. {
  45. const loader = new THREE.FontLoader();
  46. const fontURL = '../assets/helvetiker_bold.typeface.json';
  47. const font = await loader.loadAsync(fontURL);
  48. const material = new THREE.MeshPhongMaterial({ color });
  49. const geometry = new THREE.TextGeometry(text, {
  50. font: font,
  51. size: 0.3,
  52. height: 0.05,
  53. });
  54. const mesh = new THREE.Mesh(geometry, material);
  55. ar.root.add(mesh);
  56. return mesh;
  57. }
  58. function createPointer(ar)
  59. {
  60. const pointer = {
  61. position: new THREE.Vector2(),
  62. down: false,
  63. get active()
  64. {
  65. return this.down && Math.max(Math.abs(this.position.x), Math.abs(this.position.y)) <= 1.0;
  66. }
  67. };
  68. function updatePosition(event)
  69. {
  70. const canvas = ar.renderer.domElement;
  71. const rect = canvas.getBoundingClientRect();
  72. const x = event.pageX - (rect.left + window.scrollX);
  73. const y = event.pageY - (rect.top + window.scrollY);
  74. // normalize to [-1,1] x [-1,1]
  75. pointer.position.x = 2.0 * x / rect.width - 1.0;
  76. pointer.position.y = -2.0 * y / rect.height + 1.0;
  77. }
  78. // setup pointer interactivity
  79. window.addEventListener('pointermove', event => {
  80. updatePosition(event);
  81. });
  82. window.addEventListener('pointerdown', event => {
  83. updatePosition(event);
  84. pointer.down = true;
  85. });
  86. window.addEventListener('pointerup', event => {
  87. pointer.down = false;
  88. });
  89. // done!
  90. return pointer;
  91. }
  92. function animate(ar)
  93. {
  94. // reset all cubes
  95. for(let i = 0; i < my.cubes.length; i++) {
  96. my.cubes[i].material.color.setHex(my.cubes[i].userData.color);
  97. my.cubes[i].scale.z = 1.0;
  98. }
  99. // interact with objects via raycasting
  100. if(my.pointer.active) {
  101. my.raycaster.setFromCamera(my.pointer.position, ar.camera);
  102. const intersections = my.raycaster.intersectObjects(my.cubes);
  103. for(let i = 0; i < intersections.length; i++) {
  104. // create the appearance of a pushed button
  105. const object = intersections[i].object;
  106. object.material.color.setHex(0xff3333);
  107. object.scale.z = 0.2;
  108. }
  109. }
  110. }
  111. async function startARSession()
  112. {
  113. if(!Martins.isSupported()) {
  114. throw new Error(
  115. 'Use a browser/device compatible with WebGL2 and WebAssembly. ' +
  116. 'Your user agent is ' + navigator.userAgent
  117. );
  118. }
  119. //Martins.Settings.powerPreference = 'low-power';
  120. const tracker = Martins.Tracker.ImageTracker();
  121. await tracker.database.add([{
  122. name: 'my-reference-image',
  123. image: document.getElementById('my-reference-image')
  124. }]);
  125. const viewport = Martins.Viewport({
  126. container: document.getElementById('ar-viewport'),
  127. hudContainer: document.getElementById('ar-hud')
  128. });
  129. const video = document.getElementById('my-video');
  130. const useWebcam = (video === null);
  131. const source = useWebcam ?
  132. Martins.Source.Camera({ resolution: 'md' }) :
  133. Martins.Source.Video(video);
  134. const session = await Martins.startSession({
  135. mode: 'immersive',
  136. viewport: viewport,
  137. trackers: [ tracker ],
  138. sources: [ source ],
  139. stats: true,
  140. gizmos: true,
  141. });
  142. const scan = document.getElementById('scan');
  143. tracker.addEventListener('targetfound', event => {
  144. session.gizmos.visible = false;
  145. if(scan)
  146. scan.hidden = true;
  147. });
  148. tracker.addEventListener('targetlost', event => {
  149. session.gizmos.visible = true;
  150. if(scan)
  151. scan.hidden = false;
  152. });
  153. return session;
  154. }
  155. // link MARTINS.js to THREE.js
  156. linkMartinsToTHREE(startARSession, animate, initialize);
  157. });