VRControls.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647
  1. import * as THREE from "../../libs/three.js/build/three.module.js";
  2. import {EventDispatcher} from "../EventDispatcher.js";
  3. import { XRControllerModelFactory } from '../../libs/three.js/webxr/XRControllerModelFactory.js';
  4. import {Line2} from "../../libs/three.js/lines/Line2.js";
  5. import {LineGeometry} from "../../libs/three.js/lines/LineGeometry.js";
  6. import {LineMaterial} from "../../libs/three.js/lines/LineMaterial.js";
  7. let fakeCam = new THREE.PerspectiveCamera();
  8. function toScene(vec, ref){
  9. let node = ref.clone();
  10. node.updateMatrix();
  11. node.updateMatrixWorld();
  12. let result = vec.clone().applyMatrix4(node.matrix);
  13. result.z -= 0.8 * node.scale.x;
  14. return result;
  15. };
  16. function computeMove(vrControls, controller){
  17. if(!controller || !controller.inputSource || !controller.inputSource.gamepad){
  18. return null;
  19. }
  20. let pad = controller.inputSource.gamepad;
  21. let axes = pad.axes;
  22. // [0,1] are for touchpad, [2,3] for thumbsticks?
  23. let y = 0;
  24. if(axes.length === 2){
  25. y = axes[1];
  26. }else if(axes.length === 4){
  27. y = axes[3];
  28. }
  29. y = Math.sign(y) * (2 * y) ** 2;
  30. let maxSize = 0;
  31. for(let pc of viewer.scene.pointclouds){
  32. let size = pc.boundingBox.min.distanceTo(pc.boundingBox.max);
  33. maxSize = Math.max(maxSize, size);
  34. }
  35. let multiplicator = Math.pow(maxSize, 0.5) / 2;
  36. let scale = vrControls.node.scale.x;
  37. let moveSpeed = viewer.getMoveSpeed();
  38. let amount = multiplicator * y * (moveSpeed ** 0.5) / scale;
  39. let rotation = new THREE.Quaternion().setFromEuler(controller.rotation);
  40. let dir = new THREE.Vector3(0, 0, -1);
  41. dir.applyQuaternion(rotation);
  42. let move = dir.clone().multiplyScalar(amount);
  43. let p1 = vrControls.toScene(controller.position);
  44. let p2 = vrControls.toScene(controller.position.clone().add(move));
  45. move = p2.clone().sub(p1);
  46. return move;
  47. };
  48. class FlyMode{
  49. constructor(vrControls){
  50. this.moveFactor = 1;
  51. this.dbgLabel = null;
  52. }
  53. start(vrControls){
  54. if(!this.dbgLabel){
  55. /* this.dbgLabel = new Potree.TextSprite("abc");
  56. this.dbgLabel.name = "debug label";
  57. vrControls.viewer.sceneVR.add(this.dbgLabel);
  58. this.dbgLabel.visible = false; */
  59. }
  60. }
  61. end(){
  62. }
  63. update(vrControls, delta){
  64. let primary = vrControls.cPrimary;
  65. let secondary = vrControls.cSecondary;
  66. let move1 = computeMove(vrControls, primary);
  67. let move2 = computeMove(vrControls, secondary);
  68. if(!move1){
  69. move1 = new THREE.Vector3();
  70. }
  71. if(!move2){
  72. move2 = new THREE.Vector3();
  73. }
  74. let move = move1.clone().add(move2);
  75. move.multiplyScalar(-delta * this.moveFactor);
  76. vrControls.node.position.add(move);
  77. let scale = vrControls.node.scale.x;
  78. let camVR = vrControls.viewer.renderer.xr.getCamera(fakeCam);
  79. let vrPos = camVR.getWorldPosition(new THREE.Vector3());
  80. let vrDir = camVR.getWorldDirection(new THREE.Vector3());
  81. let vrTarget = vrPos.clone().add(vrDir.multiplyScalar(scale));
  82. let scenePos = toScene(vrPos, vrControls.node);
  83. let sceneDir = toScene(vrPos.clone().add(vrDir), vrControls.node).sub(scenePos);
  84. sceneDir.normalize().multiplyScalar(scale);
  85. let sceneTarget = scenePos.clone().add(sceneDir);
  86. vrControls.viewer.scene.view.setView(scenePos, sceneTarget);
  87. if(Potree.debug.message){
  88. this.dbgLabel.visible = true;
  89. this.dbgLabel.setText(Potree.debug.message);
  90. this.dbgLabel.scale.set(0.1, 0.1, 0.1);
  91. this.dbgLabel.position.copy(primary.position);
  92. }
  93. }
  94. };
  95. class TranslationMode{
  96. constructor(){
  97. this.controller = null;
  98. this.startPos = null;
  99. this.debugLine = null;
  100. }
  101. start(vrControls){
  102. this.controller = vrControls.triggered.values().next().value;
  103. this.startPos = vrControls.node.position.clone();
  104. }
  105. end(vrControls){
  106. }
  107. update(vrControls, delta){
  108. let start = this.controller.start.position;
  109. let end = this.controller.position;
  110. start = vrControls.toScene(start);
  111. end = vrControls.toScene(end);
  112. let diff = end.clone().sub(start);
  113. diff.set(-diff.x, -diff.y, -diff.z);
  114. let pos = new THREE.Vector3().addVectors(this.startPos, diff);
  115. vrControls.node.position.copy(pos);
  116. }
  117. };
  118. class RotScaleMode{
  119. constructor(){
  120. this.line = null;
  121. this.startState = null;
  122. }
  123. start(vrControls){
  124. if(!this.line){
  125. this.line = Potree.Utils.debugLine(
  126. vrControls.viewer.sceneVR,
  127. new THREE.Vector3(0, 0, 0),
  128. new THREE.Vector3(0, 0, 0),
  129. 0xffff00,
  130. );
  131. this.dbgLabel = new Potree.TextSprite("abc");
  132. this.dbgLabel.scale.set(0.1, 0.1, 0.1);
  133. vrControls.viewer.sceneVR.add(this.dbgLabel);
  134. }
  135. this.line.node.visible = true;
  136. this.startState = vrControls.node.clone();
  137. }
  138. end(vrControls){
  139. this.line.node.visible = false;
  140. this.dbgLabel.visible = false;
  141. }
  142. update(vrControls, delta){
  143. let start_c1 = vrControls.cPrimary.start.position.clone();
  144. let start_c2 = vrControls.cSecondary.start.position.clone();
  145. let start_center = start_c1.clone().add(start_c2).multiplyScalar(0.5);
  146. let start_c1_c2 = start_c2.clone().sub(start_c1);
  147. let end_c1 = vrControls.cPrimary.position.clone();
  148. let end_c2 = vrControls.cSecondary.position.clone();
  149. let end_center = end_c1.clone().add(end_c2).multiplyScalar(0.5);
  150. let end_c1_c2 = end_c2.clone().sub(end_c1);
  151. let d1 = start_c1_c2.length();
  152. let d2 = end_c1_c2.length();
  153. let angleStart = new THREE.Vector2(start_c1_c2.x, start_c1_c2.z).angle();
  154. let angleEnd = new THREE.Vector2(end_c1_c2.x, end_c1_c2.z).angle();
  155. let angleDiff = angleEnd - angleStart;
  156. let scale = d2 / d1;
  157. let node = this.startState.clone();
  158. node.updateMatrix();
  159. node.matrixAutoUpdate = false;
  160. let mToOrigin = new THREE.Matrix4().makeTranslation(...toScene(start_center, this.startState).multiplyScalar(-1).toArray());
  161. let mToStart = new THREE.Matrix4().makeTranslation(...toScene(start_center, this.startState).toArray());
  162. let mRotate = new THREE.Matrix4().makeRotationZ(angleDiff);
  163. let mScale = new THREE.Matrix4().makeScale(1 / scale, 1 / scale, 1 / scale);
  164. node.applyMatrix4(mToOrigin);
  165. node.applyMatrix4(mRotate);
  166. node.applyMatrix4(mScale);
  167. node.applyMatrix4(mToStart);
  168. let oldScenePos = toScene(start_center, this.startState);
  169. let newScenePos = toScene(end_center, node);
  170. let toNew = oldScenePos.clone().sub(newScenePos);
  171. let mToNew = new THREE.Matrix4().makeTranslation(...toNew.toArray());
  172. node.applyMatrix4(mToNew);
  173. node.matrix.decompose(node.position, node.quaternion, node.scale );
  174. vrControls.node.position.copy(node.position);
  175. vrControls.node.quaternion.copy(node.quaternion);
  176. vrControls.node.scale.copy(node.scale);
  177. vrControls.node.updateMatrix();
  178. {
  179. let scale = vrControls.node.scale.x;
  180. let camVR = vrControls.viewer.renderer.xr.getCamera(fakeCam);
  181. let vrPos = camVR.getWorldPosition(new THREE.Vector3());
  182. let vrDir = camVR.getWorldDirection(new THREE.Vector3());
  183. let vrTarget = vrPos.clone().add(vrDir.multiplyScalar(scale));
  184. let scenePos = toScene(vrPos, this.startState);
  185. let sceneDir = toScene(vrPos.clone().add(vrDir), this.startState).sub(scenePos);
  186. sceneDir.normalize().multiplyScalar(scale);
  187. let sceneTarget = scenePos.clone().add(sceneDir);
  188. vrControls.viewer.scene.view.setView(scenePos, sceneTarget);
  189. vrControls.viewer.setMoveSpeed(scale);
  190. }
  191. { // update "GUI"
  192. this.line.set(end_c1, end_c2);
  193. let scale = vrControls.node.scale.x;
  194. this.dbgLabel.visible = true;
  195. this.dbgLabel.position.copy(end_center);
  196. this.dbgLabel.setText(`scale: 1 : ${scale.toFixed(2)}`);
  197. this.dbgLabel.scale.set(0.05, 0.05, 0.05);
  198. }
  199. }
  200. };
  201. export class VRControls extends EventDispatcher{
  202. constructor(viewer){
  203. super(viewer);
  204. this.viewer = viewer;
  205. viewer.addEventListener("vr_start", this.onStart.bind(this));
  206. viewer.addEventListener("vr_end", this.onEnd.bind(this));
  207. this.node = new THREE.Object3D();
  208. this.node.up.set(0, 0, 1);
  209. this.triggered = new Set();
  210. let xr = viewer.renderer.xr;
  211. { // lights
  212. const light = new THREE.PointLight( 0xffffff, 5, 0, 1 );
  213. light.position.set(0, 2, 0);
  214. this.viewer.sceneVR.add(light)
  215. }
  216. this.menu = null;
  217. const controllerModelFactory = new XRControllerModelFactory();
  218. let sg = new THREE.SphereGeometry(1, 32, 32);
  219. let sm = new THREE.MeshNormalMaterial();
  220. { // setup primary controller
  221. let controller = xr.getController(0);
  222. let grip = xr.getControllerGrip(0);
  223. grip.name = "grip(0)";
  224. // ADD CONTROLLERMODEL
  225. grip.add( controllerModelFactory.createControllerModel( grip ) );
  226. this.viewer.sceneVR.add(grip);
  227. // ADD SPHERE
  228. let sphere = new THREE.Mesh(sg, sm);
  229. sphere.scale.set(0.005, 0.005, 0.005);
  230. controller.add(sphere);
  231. controller.visible = true;
  232. this.viewer.sceneVR.add(controller);
  233. { // ADD LINE
  234. let lineGeometry = new LineGeometry();
  235. lineGeometry.setPositions([
  236. 0, 0, -0.15,
  237. 0, 0, 0.05,
  238. ]);
  239. let lineMaterial = new LineMaterial({
  240. color: 0xff0000,
  241. linewidth: 2,
  242. resolution: new THREE.Vector2(1000, 1000),
  243. });
  244. const line = new Line2(lineGeometry, lineMaterial);
  245. controller.add(line);
  246. }
  247. controller.addEventListener( 'connected', function ( event ) {
  248. const xrInputSource = event.data;
  249. controller.inputSource = xrInputSource;
  250. // initInfo(controller);
  251. });
  252. controller.addEventListener( 'selectstart', () => {this.onTriggerStart(controller)});
  253. controller.addEventListener( 'selectend', () => {this.onTriggerEnd(controller)});
  254. this.cPrimary = controller;
  255. }
  256. { // setup secondary controller
  257. let controller = xr.getController(1);
  258. let grip = xr.getControllerGrip(1);
  259. // ADD CONTROLLER MODEL
  260. let model = controllerModelFactory.createControllerModel( grip );
  261. grip.add(model);
  262. this.viewer.sceneVR.add( grip );
  263. // ADD SPHERE
  264. let sphere = new THREE.Mesh(sg, sm);
  265. sphere.scale.set(0.005, 0.005, 0.005);
  266. controller.add(sphere);
  267. controller.visible = true;
  268. this.viewer.sceneVR.add(controller);
  269. { // ADD LINE
  270. let lineGeometry = new LineGeometry();
  271. lineGeometry.setPositions([
  272. 0, 0, -0.15,
  273. 0, 0, 0.05,
  274. ]);
  275. let lineMaterial = new LineMaterial({
  276. color: 0xff0000,
  277. linewidth: 2,
  278. resolution: new THREE.Vector2(1000, 1000),
  279. });
  280. const line = new Line2(lineGeometry, lineMaterial);
  281. controller.add(line);
  282. }
  283. controller.addEventListener( 'connected', (event) => {
  284. const xrInputSource = event.data;
  285. controller.inputSource = xrInputSource;
  286. this.initMenu(controller);
  287. });
  288. controller.addEventListener( 'selectstart', () => {this.onTriggerStart(controller)});
  289. controller.addEventListener( 'selectend', () => {this.onTriggerEnd(controller)});
  290. this.cSecondary = controller;
  291. }
  292. this.mode_fly = new FlyMode();
  293. this.mode_translate = new TranslationMode();
  294. this.mode_rotScale = new RotScaleMode();
  295. this.setMode(this.mode_fly);
  296. }
  297. createSlider(label, min, max){
  298. let sg = new THREE.SphereGeometry(1, 8, 8);
  299. let cg = new THREE.CylinderGeometry(1, 1, 1, 8);
  300. let matHandle = new THREE.MeshBasicMaterial({color: 0xff0000});
  301. let matScale = new THREE.MeshBasicMaterial({color: 0xff4444});
  302. let matValue = new THREE.MeshNormalMaterial();
  303. let node = new THREE.Object3D("slider");
  304. let nLabel = new Potree.TextSprite(`${label}: 0`);
  305. let nMax = new THREE.Mesh(sg, matHandle);
  306. let nMin = new THREE.Mesh(sg, matHandle);
  307. let nValue = new THREE.Mesh(sg, matValue);
  308. let nScale = new THREE.Mesh(cg, matScale);
  309. nLabel.scale.set(0.2, 0.2, 0.2);
  310. nLabel.position.set(0, 0.35, 0);
  311. nMax.scale.set(0.02, 0.02, 0.02);
  312. nMax.position.set(0, 0.25, 0);
  313. nMin.scale.set(0.02, 0.02, 0.02);
  314. nMin.position.set(0, -0.25, 0);
  315. nValue.scale.set(0.02, 0.02, 0.02);
  316. nValue.position.set(0, 0, 0);
  317. nScale.scale.set(0.005, 0.5, 0.005);
  318. node.add(nLabel);
  319. node.add(nMax);
  320. node.add(nMin);
  321. node.add(nValue);
  322. node.add(nScale);
  323. return node;
  324. }
  325. createInfo(){
  326. let texture = new THREE.TextureLoader().load(`${Potree.resourcePath}/images/vr_controller_help.jpg`);
  327. let plane = new THREE.PlaneBufferGeometry(1, 1, 1, 1);
  328. let infoMaterial = new THREE.MeshBasicMaterial({map: texture});
  329. let infoNode = new THREE.Mesh(plane, infoMaterial);
  330. return infoNode;
  331. }
  332. initMenu(controller){
  333. if(this.menu){
  334. return;
  335. }
  336. let node = new THREE.Object3D("vr menu");
  337. // let nSlider = this.createSlider("speed", 0, 1);
  338. // let nInfo = this.createInfo();
  339. // // node.add(nSlider);
  340. // node.add(nInfo);
  341. // {
  342. // node.rotation.set(-1.5, 0, 0)
  343. // node.scale.set(0.3, 0.3, 0.3);
  344. // node.position.set(-0.2, -0.002, -0.1)
  345. // // nInfo.position.set(0.5, 0, 0);
  346. // nInfo.scale.set(0.8, 0.6, 0);
  347. // // controller.add(node);
  348. // }
  349. // node.position.set(-0.3, 1.2, 0.2);
  350. // node.scale.set(0.3, 0.2, 0.3);
  351. // node.lookAt(new THREE.Vector3(0, 1.5, 0.1));
  352. // this.viewer.sceneVR.add(node);
  353. this.menu = node;
  354. // window.vrSlider = nSlider;
  355. window.vrMenu = node;
  356. }
  357. toScene(vec){
  358. let camVR = this.getCamera();
  359. let mat = camVR.matrixWorld;
  360. let result = vec.clone().applyMatrix4(mat);
  361. return result;
  362. }
  363. toVR(vec){
  364. let camVR = this.getCamera();
  365. let mat = camVR.matrixWorld.clone();
  366. mat.invert();
  367. let result = vec.clone().applyMatrix4(mat);
  368. return result;
  369. }
  370. setMode(mode){
  371. if(this.mode === mode){
  372. return;
  373. }
  374. if(this.mode){
  375. this.mode.end(this);
  376. }
  377. for(let controller of [this.cPrimary, this.cSecondary]){
  378. let start = {
  379. position: controller.position.clone(),
  380. rotation: controller.rotation.clone(),
  381. };
  382. controller.start = start;
  383. }
  384. this.mode = mode;
  385. this.mode.start(this);
  386. }
  387. onTriggerStart(controller){
  388. this.triggered.add(controller);
  389. if(this.triggered.size === 0){
  390. this.setMode(this.mode_fly);
  391. }else if(this.triggered.size === 1){
  392. this.setMode(this.mode_translate);
  393. }else if(this.triggered.size === 2){
  394. this.setMode(this.mode_rotScale);
  395. }
  396. }
  397. onTriggerEnd(controller){
  398. this.triggered.delete(controller);
  399. if(this.triggered.size === 0){
  400. this.setMode(this.mode_fly);
  401. }else if(this.triggered.size === 1){
  402. this.setMode(this.mode_translate);
  403. }else if(this.triggered.size === 2){
  404. this.setMode(this.mode_rotScale);
  405. }
  406. }
  407. onStart(){
  408. let position = this.viewer.scene.view.position.clone();
  409. let direction = this.viewer.scene.view.direction;
  410. direction.multiplyScalar(-1);
  411. let target = position.clone().add(direction);
  412. target.z = position.z;
  413. let scale = this.viewer.getMoveSpeed();
  414. this.node.position.copy(position);
  415. this.node.lookAt(target);
  416. this.node.scale.set(scale, scale, scale);
  417. this.node.updateMatrix();
  418. this.node.updateMatrixWorld();
  419. }
  420. onEnd(){
  421. }
  422. setScene(scene){
  423. this.scene = scene;
  424. }
  425. getCamera(){
  426. let reference = this.viewer.scene.getActiveCamera();
  427. let camera = new THREE.PerspectiveCamera();
  428. // let scale = this.node.scale.x;
  429. let scale = this.viewer.getMoveSpeed();
  430. //camera.near = 0.01 / scale;
  431. camera.near = 0.1;
  432. camera.far = 1000;
  433. // camera.near = reference.near / scale;
  434. // camera.far = reference.far / scale;
  435. camera.up.set(0, 0, 1);
  436. camera.lookAt(new THREE.Vector3(0, -1, 0));
  437. camera.updateMatrix();
  438. camera.updateMatrixWorld();
  439. camera.position.copy(this.node.position);
  440. camera.rotation.copy(this.node.rotation);
  441. camera.scale.set(scale, scale, scale);
  442. camera.updateMatrix();
  443. camera.updateMatrixWorld();
  444. camera.matrixAutoUpdate = false;
  445. camera.parent = camera;
  446. return camera;
  447. }
  448. update(delta){
  449. // if(this.mode === this.mode_fly){
  450. // let ray = new THREE.Ray(origin, direction);
  451. // for(let object of this.selectables){
  452. // if(object.intersectsRay(ray)){
  453. // object.onHit(ray);
  454. // }
  455. // }
  456. // }
  457. this.mode.update(this, delta);
  458. }
  459. };