From b803a7e4681af8af6b27113e60f75c4cccd121a0 Mon Sep 17 00:00:00 2001 From: NIthin Steven F Date: Fri, 19 Jul 2024 19:21:45 +0530 Subject: [PATCH 1/2] Add pointers for mouse look component , Improve pointer lock. --- mouse-look.ts | 112 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 79 insertions(+), 33 deletions(-) diff --git a/mouse-look.ts b/mouse-look.ts index 348acd1..f16625f 100644 --- a/mouse-look.ts +++ b/mouse-look.ts @@ -2,13 +2,15 @@ import {Component} from '@wonderlandengine/api'; import {property} from '@wonderlandengine/api/decorators.js'; import {vec3} from 'gl-matrix'; -const preventDefault = (e: Event) => { e.preventDefault(); }; +const preventDefault = (e: Event) => { + e.preventDefault(); +}; /** - * Controls the camera orientation through mouse movement. + * Controls the camera orientation through mouse and touch movement. * * Efficiently implemented to affect object orientation only - * when the mouse moves. + * when the mouse or touch moves. */ export class MouseLookComponent extends Component { static TypeName = 'mouse-look'; @@ -17,8 +19,8 @@ export class MouseLookComponent extends Component { @property.float(0.25) sensitity = 0.25; - /** Require a mouse button to be pressed to control view. - * Otherwise view will allways follow mouse movement */ + /** Require a mouse button or touch to be pressed to control view. + * Otherwise view will always follow mouse or touch movement */ @property.bool(true) requireMouseDown = true; @@ -38,71 +40,115 @@ export class MouseLookComponent extends Component { private rotationX = 0; private rotationY = 0; private mouseDown = false; + /** Pointerlock spec prevents calling pointerlock right after user exiting it via esc for ~1 second */ + private pointerLockCooldown = false; onActivate() { - document.addEventListener('mousemove', this.onMouseMove); - const canvas = this.engine.canvas; + if (this.mouseButtonIndex === 2) { + canvas.addEventListener('contextmenu', preventDefault, false); + } + canvas.addEventListener('pointermove', this.onPointerMove); if (this.pointerLockOnClick) { - canvas.addEventListener('mousedown', this.requestPointerLock); + canvas.addEventListener('pointerdown', this.onPointerDown); + document.addEventListener('pointerlockchange', this.onPointerLockChange); } - if (this.requireMouseDown) { - if (this.mouseButtonIndex === 2) { - canvas.addEventListener('contextmenu', preventDefault, false); - } - canvas.addEventListener('mousedown', this.onMouseDown); - canvas.addEventListener('mouseup', this.onMouseUp); + if (this.requireMouseDown && !this.pointerLockOnClick) { + canvas.addEventListener('pointerdown', this.onPointerDown); + canvas.addEventListener('pointerup', this.onPointerUp); } } onDeactivate() { - document.removeEventListener('mousemove', this.onMouseMove); - const canvas = this.engine.canvas; + canvas.removeEventListener('pointermove', this.onPointerMove); + if (this.pointerLockOnClick) { - canvas.removeEventListener('mousedown', this.requestPointerLock); + canvas.removeEventListener('pointerdown', this.onPointerDown); + document.removeEventListener('pointerlockchange', this.onPointerLockChange); } - if (this.requireMouseDown) { + if (this.requireMouseDown && !this.pointerLockOnClick) { if (this.mouseButtonIndex === 2) { - canvas.removeEventListener('contextmenu', preventDefault, false); + document.removeEventListener('contextmenu', preventDefault, false); } - canvas.removeEventListener('mousedown', this.onMouseDown); - canvas.removeEventListener('mouseup', this.onMouseUp); + canvas.removeEventListener('pointerdown', this.onPointerDown); + canvas.removeEventListener('pointerup', this.onPointerUp); } } - requestPointerLock = () => { + async requestPointerLock() { const canvas = this.engine.canvas; canvas.requestPointerLock = canvas.requestPointerLock || (canvas as any).mozRequestPointerLock || (canvas as any).webkitRequestPointerLock; - canvas.requestPointerLock(); + + try { + await navigator.locks.request('pointer-lock', async (lock) => { + if (!document.pointerLockElement) { + await canvas.requestPointerLock(); + } + }); + } catch (error) { + console.error('Pointer lock request failed:', error); + } } - onMouseDown = (e: MouseEvent) => { - if (e.button === this.mouseButtonIndex) { + onPointerLockChange = () => { + const canvas = this.engine.canvas; + if (document.pointerLockElement === canvas) { + } else { + this.mouseDown = false; + this.pointerLockCooldown = true; + document.body.style.cursor = 'initial'; + + setTimeout(() => { + this.pointerLockCooldown = false; + }, 1500); + } + }; + + onPointerDown = (e: PointerEvent) => { + if (this.pointerLockCooldown) { + return; + } + + if (e.button === this.mouseButtonIndex || e.pointerType === 'touch') { this.mouseDown = true; document.body.style.cursor = 'grabbing'; + if (e.button === 2) { + e.preventDefault(); + } + + if ( + this.pointerLockOnClick && + document.pointerLockElement !== this.engine.canvas + ) { + this.requestPointerLock(); + } if (e.button === 1) { e.preventDefault(); /* Prevent scrolling */ return false; } } - } + }; - onMouseUp = (e: MouseEvent) => { - if (e.button === this.mouseButtonIndex) { + onPointerUp = (e: PointerEvent) => { + if (e.button === this.mouseButtonIndex || e.pointerType === 'touch') { this.mouseDown = false; document.body.style.cursor = 'initial'; } - } - - onMouseMove = (e: MouseEvent) => { - if (this.active && (this.mouseDown || !this.requireMouseDown)) { + }; + + onPointerMove = (e: PointerEvent) => { + if ( + this.active && + (this.mouseDown || !this.requireMouseDown) && + (!this.pointerLockOnClick || document.pointerLockElement === this.engine.canvas) + ) { this.rotationY = (-this.sensitity * e.movementX) / 100; this.rotationX = (-this.sensitity * e.movementY) / 100; @@ -126,5 +172,5 @@ export class MouseLookComponent extends Component { this.object.rotateAxisAngleRadLocal([0, 1, 0], this.currentRotationY); this.object.translateLocal(this.origin); } - } + }; } From a4a0ab2cf85dfe47aa16053a1df1a5992afc4655 Mon Sep 17 00:00:00 2001 From: NIthin Steven F Date: Thu, 25 Jul 2024 20:20:17 +0530 Subject: [PATCH 2/2] Add early returns and minor improvements --- mouse-look.ts | 60 +++++++++++++++++++++------------------------------ 1 file changed, 25 insertions(+), 35 deletions(-) diff --git a/mouse-look.ts b/mouse-look.ts index f16625f..762ccd1 100644 --- a/mouse-look.ts +++ b/mouse-look.ts @@ -71,7 +71,7 @@ export class MouseLookComponent extends Component { if (this.requireMouseDown && !this.pointerLockOnClick) { if (this.mouseButtonIndex === 2) { - document.removeEventListener('contextmenu', preventDefault, false); + canvas.removeEventListener('contextmenu', preventDefault, false); } canvas.removeEventListener('pointerdown', this.onPointerDown); canvas.removeEventListener('pointerup', this.onPointerUp); @@ -98,41 +98,35 @@ export class MouseLookComponent extends Component { onPointerLockChange = () => { const canvas = this.engine.canvas; - if (document.pointerLockElement === canvas) { - } else { - this.mouseDown = false; - this.pointerLockCooldown = true; - document.body.style.cursor = 'initial'; - - setTimeout(() => { - this.pointerLockCooldown = false; - }, 1500); - } + if (document.pointerLockElement === canvas) return; + this.mouseDown = false; + this.pointerLockCooldown = true; + document.body.style.cursor = 'initial'; + + setTimeout(() => { + this.pointerLockCooldown = false; + }, 1500); }; onPointerDown = (e: PointerEvent) => { - if (this.pointerLockCooldown) { + if ( + this.pointerLockCooldown || + !(e.button === this.mouseButtonIndex || e.pointerType === 'touch') + ) return; + this.mouseDown = true; + document.body.style.cursor = 'grabbing'; + if (e.button === 2) { + e.preventDefault(); } - if (e.button === this.mouseButtonIndex || e.pointerType === 'touch') { - this.mouseDown = true; - document.body.style.cursor = 'grabbing'; - if (e.button === 2) { - e.preventDefault(); - } - - if ( - this.pointerLockOnClick && - document.pointerLockElement !== this.engine.canvas - ) { - this.requestPointerLock(); - } - if (e.button === 1) { - e.preventDefault(); - /* Prevent scrolling */ - return false; - } + if (this.pointerLockOnClick && document.pointerLockElement !== this.engine.canvas) { + this.requestPointerLock(); + } + if (e.button === 1) { + e.preventDefault(); + /* Prevent scrolling */ + return false; } }; @@ -144,11 +138,7 @@ export class MouseLookComponent extends Component { }; onPointerMove = (e: PointerEvent) => { - if ( - this.active && - (this.mouseDown || !this.requireMouseDown) && - (!this.pointerLockOnClick || document.pointerLockElement === this.engine.canvas) - ) { + if (this.active && (this.mouseDown || !this.requireMouseDown)) { this.rotationY = (-this.sensitity * e.movementX) / 100; this.rotationX = (-this.sensitity * e.movementY) / 100;