@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
158 lines (157 loc) • 5.48 kB
JavaScript
;
import { Euler, EventDispatcher, Vector3, Spherical } from "three";
import { CorePlayerKeyEvents } from "../../../core/player/KeyEvents";
const changeEvent = { type: "change" };
const lockEvent = { type: "lock" };
const unlockEvent = { type: "unlock" };
const PI_2 = Math.PI / 2;
const tmpCameraUnproject = new Vector3();
const spherical = new Spherical();
const LOCK_ELEMENT_DEFAULT_HTML = `
<div style="
text-align:center;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
cursor: pointer;
padding: 5px 10px;
background:gray;
border:white;
color: white;
">
<div style="font-size: 1rem">CLICK TO START</div>
<div style="font-size: 0.6rem">press ESC to show your cursor</div>
</div>
`;
export class PointerLockControls extends EventDispatcher {
constructor(camera, domElement, options, player) {
super();
this.camera = camera;
this.domElement = domElement;
this.options = options;
this.player = player;
this.isLocked = false;
this.minPolarAngle = 0;
// radians
this.maxPolarAngle = Math.PI;
// radians
this.rotateSpeed = 1;
this.euler = new Euler(0, 0, 0, "YXZ");
this.boundMethods = {
lock: this.lock.bind(this),
onMouseMove: this.onMouseMove.bind(this),
onPointerlockChange: this.onPointerlockChange.bind(this),
onPointerlockError: this.onPointerlockError.bind(this)
};
this._azimuthalAngle = 0;
this.connect();
this._showUnlockHTMLElement();
}
onMouseMove(event) {
if (this.isLocked === false)
return;
var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;
this.euler.setFromQuaternion(this.camera.quaternion);
this.euler.y -= movementX * 2e-3 * this.rotateSpeed;
this.euler.x -= movementY * 2e-3 * this.rotateSpeed;
this.euler.x = Math.max(PI_2 - this.maxPolarAngle, Math.min(PI_2 - this.minPolarAngle, this.euler.x));
this.camera.quaternion.setFromEuler(this.euler);
this._computeAzimuthalAngle();
this.dispatchEvent(changeEvent);
}
_computeAzimuthalAngle() {
this.camera.updateMatrixWorld();
tmpCameraUnproject.set(0, 0, 1);
this.camera.localToWorld(tmpCameraUnproject);
tmpCameraUnproject.sub(this.camera.position);
spherical.setFromVector3(tmpCameraUnproject);
this._azimuthalAngle = spherical.theta;
}
onPointerlockChange() {
var _a, _b;
if (this.domElement.ownerDocument.pointerLockElement === this.domElement) {
this.dispatchEvent(lockEvent);
this.isLocked = true;
this._removeHTMLElement();
if (this.player) {
this._corePlayerKeyEvents = this._corePlayerKeyEvents || new CorePlayerKeyEvents(this.player);
this._corePlayerKeyEvents.addEvents();
}
} else {
this.dispatchEvent(unlockEvent);
this.isLocked = false;
this._showUnlockHTMLElement();
(_a = this._corePlayerKeyEvents) == null ? void 0 : _a.removeEvents();
(_b = this.player) == null ? void 0 : _b.stop();
}
}
onPointerlockError() {
console.error(
"THREE.PointerLockControls: Unable to use Pointer Lock API (Note that you need to wait for 2 seconds to lock the pointer after having just unlocked it)"
);
}
connect() {
this.domElement.ownerDocument.addEventListener("mousemove", this.boundMethods.onMouseMove);
this.domElement.ownerDocument.addEventListener("pointerlockchange", this.boundMethods.onPointerlockChange);
this.domElement.ownerDocument.addEventListener("pointerlockerror", this.boundMethods.onPointerlockError);
}
disconnect() {
this.domElement.ownerDocument.removeEventListener("mousemove", this.boundMethods.onMouseMove);
this.domElement.ownerDocument.removeEventListener("pointerlockchange", this.boundMethods.onPointerlockChange);
this.domElement.ownerDocument.removeEventListener("pointerlockerror", this.boundMethods.onPointerlockError);
}
dispose() {
this.disconnect();
this._removeHTMLElement();
}
getObject() {
return this.camera;
}
lock() {
this.domElement.requestPointerLock();
}
unlock() {
this.domElement.ownerDocument.exitPointerLock();
}
update(delta) {
if (this.player) {
this.player.setAzimuthalAngle(this._azimuthalAngle);
this.player.update(delta);
}
}
_unlockHTMLElementParent() {
return this.domElement.parentElement;
}
_unlockHTMLElement() {
return this.__unlockHTMLElement = this.__unlockHTMLElement || this._getUnlockHTMLElement();
}
_showUnlockHTMLElement() {
var _a;
const el = this._unlockHTMLElement();
if (!el) {
return;
}
(_a = this._unlockHTMLElementParent()) == null ? void 0 : _a.append(el);
}
_getUnlockHTMLElement() {
const element = this.options.lockHTMLElement || this._createUnlockHTMLElement();
element.addEventListener("pointerdown", this.boundMethods.lock);
return element;
}
_createUnlockHTMLElement() {
const el = document.createElement("div");
el.innerHTML = LOCK_ELEMENT_DEFAULT_HTML;
return el;
}
_removeHTMLElement() {
var _a;
if (!this.__unlockHTMLElement) {
return;
}
(_a = this._unlockHTMLElementParent()) == null ? void 0 : _a.removeChild(this.__unlockHTMLElement);
this.__unlockHTMLElement.removeEventListener("pointerdown", this.boundMethods.lock);
this.__unlockHTMLElement = void 0;
}
}