Skip to content

Add pointers for mouse look component , Improve pointer lock. #64

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 73 additions & 37 deletions mouse-look.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;

Expand All @@ -38,70 +40,104 @@ 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 */
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have a link for that? Better would be to know the exact moment a next pointer lock is possible. I would bet it's after flushing the task queue, rather than a delay?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DavidPeicho I learned about it in a quora discussion , was not able to find its official documentation.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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);
Comment on lines -64 to +68
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pointerlock only really makes sense in a mouse on desktop context, though, right? Then having touch input request pointer lock isn't really desired.

Copy link
Collaborator Author

@NSTCG NSTCG Jul 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Squareys How do you suggest we filter it out on mobile devies ? maybe a isMobile check ?

document.removeEventListener('pointerlockchange', this.onPointerLockChange);
}

if (this.requireMouseDown) {
if (this.requireMouseDown && !this.pointerLockOnClick) {
if (this.mouseButtonIndex === 2) {
canvas.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();
}

onMouseDown = (e: MouseEvent) => {
if (e.button === this.mouseButtonIndex) {
this.mouseDown = true;
document.body.style.cursor = 'grabbing';
if (e.button === 1) {
e.preventDefault();
/* Prevent scrolling */
return false;
}
try {
await navigator.locks.request('pointer-lock', async (lock) => {
if (!document.pointerLockElement) {
await canvas.requestPointerLock();
}
});
} catch (error) {
console.error('Pointer lock request failed:', error);
}
}

onMouseUp = (e: MouseEvent) => {
if (e.button === this.mouseButtonIndex) {
onPointerLockChange = () => {
const canvas = this.engine.canvas;
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 ||
!(e.button === this.mouseButtonIndex || e.pointerType === 'touch')
)
return;
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;
}
};

onPointerUp = (e: PointerEvent) => {
if (e.button === this.mouseButtonIndex || e.pointerType === 'touch') {
this.mouseDown = false;
document.body.style.cursor = 'initial';
}
}
};

onMouseMove = (e: MouseEvent) => {
onPointerMove = (e: PointerEvent) => {
if (this.active && (this.mouseDown || !this.requireMouseDown)) {
this.rotationY = (-this.sensitity * e.movementX) / 100;
this.rotationX = (-this.sensitity * e.movementY) / 100;
Expand All @@ -126,5 +162,5 @@ export class MouseLookComponent extends Component {
this.object.rotateAxisAngleRadLocal([0, 1, 0], this.currentRotationY);
this.object.translateLocal(this.origin);
}
}
};
}