shared-orbitcontrols.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. import * as THREE from 'https://cdn.skypack.dev/[email protected]/build/three.module.js';
  2. import { OrbitControls } from 'https://cdn.skypack.dev/[email protected]/examples/jsm/controls/OrbitControls.js';
  3. export function init(data) { /* eslint-disable-line no-unused-vars */
  4. const {canvas, inputElement} = data;
  5. const renderer = new THREE.WebGLRenderer({canvas});
  6. const fov = 75;
  7. const aspect = 2; // the canvas default
  8. const near = 0.1;
  9. const far = 100;
  10. const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
  11. camera.position.z = 4;
  12. const controls = new OrbitControls(camera, inputElement);
  13. controls.target.set(0, 0, 0);
  14. controls.update();
  15. const scene = new THREE.Scene();
  16. {
  17. const color = 0xFFFFFF;
  18. const intensity = 1;
  19. const light = new THREE.DirectionalLight(color, intensity);
  20. light.position.set(-1, 2, 4);
  21. scene.add(light);
  22. }
  23. const boxWidth = 1;
  24. const boxHeight = 1;
  25. const boxDepth = 1;
  26. const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
  27. function makeInstance(geometry, color, x) {
  28. const material = new THREE.MeshPhongMaterial({
  29. color,
  30. });
  31. const cube = new THREE.Mesh(geometry, material);
  32. scene.add(cube);
  33. cube.position.x = x;
  34. return cube;
  35. }
  36. const cubes = [
  37. makeInstance(geometry, 0x44aa88, 0),
  38. makeInstance(geometry, 0x8844aa, -2),
  39. makeInstance(geometry, 0xaa8844, 2),
  40. ];
  41. class PickHelper {
  42. constructor() {
  43. this.raycaster = new THREE.Raycaster();
  44. this.pickedObject = null;
  45. this.pickedObjectSavedColor = 0;
  46. }
  47. pick(normalizedPosition, scene, camera, time) {
  48. // restore the color if there is a picked object
  49. if (this.pickedObject) {
  50. this.pickedObject.material.emissive.setHex(this.pickedObjectSavedColor);
  51. this.pickedObject = undefined;
  52. }
  53. // cast a ray through the frustum
  54. this.raycaster.setFromCamera(normalizedPosition, camera);
  55. // get the list of objects the ray intersected
  56. const intersectedObjects = this.raycaster.intersectObjects(scene.children);
  57. if (intersectedObjects.length) {
  58. // pick the first object. It's the closest one
  59. this.pickedObject = intersectedObjects[0].object;
  60. // save its color
  61. this.pickedObjectSavedColor = this.pickedObject.material.emissive.getHex();
  62. // set its emissive color to flashing red/yellow
  63. this.pickedObject.material.emissive.setHex((time * 8) % 2 > 1 ? 0xFFFF00 : 0xFF0000);
  64. }
  65. }
  66. }
  67. const pickPosition = {x: -2, y: -2};
  68. const pickHelper = new PickHelper();
  69. clearPickPosition();
  70. function resizeRendererToDisplaySize(renderer) {
  71. const canvas = renderer.domElement;
  72. const width = inputElement.clientWidth;
  73. const height = inputElement.clientHeight;
  74. const needResize = canvas.width !== width || canvas.height !== height;
  75. if (needResize) {
  76. renderer.setSize(width, height, false);
  77. }
  78. return needResize;
  79. }
  80. function render(time) {
  81. time *= 0.001;
  82. if (resizeRendererToDisplaySize(renderer)) {
  83. camera.aspect = inputElement.clientWidth / inputElement.clientHeight;
  84. camera.updateProjectionMatrix();
  85. }
  86. cubes.forEach((cube, ndx) => {
  87. const speed = 1 + ndx * .1;
  88. const rot = time * speed;
  89. cube.rotation.x = rot;
  90. cube.rotation.y = rot;
  91. });
  92. pickHelper.pick(pickPosition, scene, camera, time);
  93. renderer.render(scene, camera);
  94. requestAnimationFrame(render);
  95. }
  96. requestAnimationFrame(render);
  97. function getCanvasRelativePosition(event) {
  98. const rect = inputElement.getBoundingClientRect();
  99. return {
  100. x: event.clientX - rect.left,
  101. y: event.clientY - rect.top,
  102. };
  103. }
  104. function setPickPosition(event) {
  105. const pos = getCanvasRelativePosition(event);
  106. pickPosition.x = (pos.x / inputElement.clientWidth ) * 2 - 1;
  107. pickPosition.y = (pos.y / inputElement.clientHeight) * -2 + 1; // note we flip Y
  108. }
  109. function clearPickPosition() {
  110. // unlike the mouse which always has a position
  111. // if the user stops touching the screen we want
  112. // to stop picking. For now we just pick a value
  113. // unlikely to pick something
  114. pickPosition.x = -100000;
  115. pickPosition.y = -100000;
  116. }
  117. inputElement.addEventListener('mousemove', setPickPosition);
  118. inputElement.addEventListener('mouseout', clearPickPosition);
  119. inputElement.addEventListener('mouseleave', clearPickPosition);
  120. inputElement.addEventListener('touchstart', (event) => {
  121. // prevent the window from scrolling
  122. event.preventDefault();
  123. setPickPosition(event.touches[0]);
  124. }, {passive: false});
  125. inputElement.addEventListener('touchmove', (event) => {
  126. setPickPosition(event.touches[0]);
  127. });
  128. inputElement.addEventListener('touchend', clearPickPosition);
  129. }