mirror of
https://github.com/pissang/claygl.git
synced 2026-02-01 17:27:08 +00:00
wip(type): add types to plugins
This commit is contained in:
parent
e7797a5f94
commit
0b8b348b7d
@ -498,7 +498,7 @@ export type OnframeCallback<T> = (target: T, percent: number) => void;
|
||||
export type AnimationPropGetter<T> = (target: T, key: string) => InterpolatableType;
|
||||
export type AnimationPropSetter<T> = (target: T, key: string, value: InterpolatableType) => void;
|
||||
|
||||
export default class ProceduralKeyframeAnimator<T> implements Animator {
|
||||
export default class ProceduralKeyframeAnimator<T = any> implements Animator {
|
||||
timeline?: Timeline;
|
||||
|
||||
targetName?: string;
|
||||
@ -846,7 +846,7 @@ export default class ProceduralKeyframeAnimator<T> implements Animator {
|
||||
* 添加动画每一帧的回调函数
|
||||
* @param callback
|
||||
*/
|
||||
during(cb: OnframeCallback<T>) {
|
||||
during(cb?: OnframeCallback<T>) {
|
||||
if (cb) {
|
||||
this._onframeCbs.push(cb);
|
||||
}
|
||||
@ -856,14 +856,14 @@ export default class ProceduralKeyframeAnimator<T> implements Animator {
|
||||
* Add callback for animation end
|
||||
* @param cb
|
||||
*/
|
||||
done(cb: DoneCallback) {
|
||||
done(cb?: DoneCallback) {
|
||||
if (cb) {
|
||||
this._doneCbs.push(cb);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
aborted(cb: AbortCallback) {
|
||||
aborted(cb?: AbortCallback) {
|
||||
if (cb) {
|
||||
this._abortedCbs.push(cb);
|
||||
}
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
// @ts-nocheck
|
||||
import Camera, { CameraOpts } from '../Camera';
|
||||
|
||||
export interface PerspectiveCameraOpts extends CameraOpts {
|
||||
|
||||
@ -132,7 +132,7 @@ class Matrix2 {
|
||||
}
|
||||
|
||||
toArray() {
|
||||
return Array.prototype.slice.call(this.array);
|
||||
return this.array.slice() as mat2.Mat2Array;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -122,7 +122,7 @@ class Matrix2d {
|
||||
}
|
||||
|
||||
toArray() {
|
||||
return Array.prototype.slice.call(this.array);
|
||||
return this.array.slice() as mat2d.Mat2dArray;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -179,7 +179,7 @@ class Matrix3 {
|
||||
}
|
||||
|
||||
toArray() {
|
||||
return Array.prototype.slice.call(this.array);
|
||||
return this.array.slice() as mat3.Mat3Array;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -379,7 +379,7 @@ class Matrix4 {
|
||||
}
|
||||
|
||||
toArray() {
|
||||
return Array.prototype.slice.call(this.array);
|
||||
return this.array.slice() as mat4.Mat4Array;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -332,7 +332,7 @@ class Quaternion {
|
||||
}
|
||||
|
||||
toArray() {
|
||||
return Array.prototype.slice.call(this.array);
|
||||
return this.array.slice() as quat.QuatArray;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -332,7 +332,7 @@ class Vector2 {
|
||||
}
|
||||
|
||||
toArray() {
|
||||
return Array.prototype.slice.call(this.array);
|
||||
return this.array.slice() as vec2.Vec2Array;
|
||||
}
|
||||
|
||||
// Supply methods that are not in place
|
||||
|
||||
@ -402,7 +402,7 @@ class Vector3 {
|
||||
}
|
||||
|
||||
toArray() {
|
||||
return Array.prototype.slice.call(this.array);
|
||||
return this.array.slice() as vec3.Vec3Array;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -328,7 +328,7 @@ class Vector4 {
|
||||
}
|
||||
|
||||
toArray() {
|
||||
return Array.prototype.slice.call(this.array);
|
||||
return this.array as vec4.Vec4Array;
|
||||
}
|
||||
/**
|
||||
* @param out
|
||||
|
||||
@ -1,12 +1,55 @@
|
||||
// @ts-nocheck
|
||||
import Base from '../core/Base';
|
||||
import Vector3 from '../math/Vector3';
|
||||
import vendor from '../core/vendor';
|
||||
import ClayNode from '../Node';
|
||||
import Timeline from '../Timeline';
|
||||
import Notifier from '../core/Notifier';
|
||||
|
||||
const doc = typeof document === 'undefined' ? {} : document;
|
||||
const addEvent = vendor.addEventListener;
|
||||
const removeEvent = vendor.removeEventListener;
|
||||
|
||||
const doc = document;
|
||||
interface FreeControlOpts {
|
||||
/**
|
||||
* Scene node to control, mostly it is a camera
|
||||
*/
|
||||
target?: ClayNode;
|
||||
/**
|
||||
* Target dom to bind with mouse events
|
||||
*/
|
||||
domElement: HTMLElement;
|
||||
|
||||
/**
|
||||
* Mouse move sensitivity
|
||||
* @type {number}
|
||||
*/
|
||||
sensitivity: number;
|
||||
|
||||
/**
|
||||
* Target move speed
|
||||
* @type {number}
|
||||
*/
|
||||
speed: number;
|
||||
|
||||
/**
|
||||
* Up axis
|
||||
* @type {clay.Vector3}
|
||||
*/
|
||||
up: Vector3;
|
||||
|
||||
/**
|
||||
* If lock vertical movement
|
||||
* @type {boolean}
|
||||
*/
|
||||
verticalMoveLock: boolean;
|
||||
|
||||
/**
|
||||
* @type {clay.Timeline}
|
||||
*/
|
||||
timeline?: Timeline;
|
||||
}
|
||||
|
||||
interface FreeControl extends FreeControlOpts {}
|
||||
/**
|
||||
* @constructor clay.plugin.FreeControl
|
||||
* @example
|
||||
* const control = new clay.plugin.FreeControl({
|
||||
* target: camera,
|
||||
@ -18,60 +61,28 @@ const doc = typeof document === 'undefined' ? {} : document;
|
||||
* renderer.render(scene, camera);
|
||||
* });
|
||||
*/
|
||||
const FreeControl = Base.extend(
|
||||
function () {
|
||||
return /** @lends clay.plugin.FreeControl# */ {
|
||||
/**
|
||||
* Scene node to control, mostly it is a camera
|
||||
* @type {clay.Node}
|
||||
*/
|
||||
target: null,
|
||||
class FreeControl extends Notifier {
|
||||
private _moveForward = false;
|
||||
private _moveBackward = false;
|
||||
private _moveLeft = false;
|
||||
private _moveRight = false;
|
||||
|
||||
/**
|
||||
* Target dom to bind with mouse events
|
||||
* @type {HTMLElement}
|
||||
*/
|
||||
domElement: null,
|
||||
private _offsetPitch = 0;
|
||||
private _offsetRoll = 0;
|
||||
|
||||
/**
|
||||
* Mouse move sensitivity
|
||||
* @type {number}
|
||||
*/
|
||||
sensitivity: 1,
|
||||
constructor(opts?: Partial<FreeControlOpts>) {
|
||||
super();
|
||||
Object.assign(
|
||||
this,
|
||||
{
|
||||
sensitivity: 1,
|
||||
speed: 0.4,
|
||||
up: new Vector3(0, 1, 0),
|
||||
verticalMoveLock: false
|
||||
},
|
||||
opts
|
||||
);
|
||||
|
||||
/**
|
||||
* Target move speed
|
||||
* @type {number}
|
||||
*/
|
||||
speed: 0.4,
|
||||
|
||||
/**
|
||||
* Up axis
|
||||
* @type {clay.Vector3}
|
||||
*/
|
||||
up: new Vector3(0, 1, 0),
|
||||
|
||||
/**
|
||||
* If lock vertical movement
|
||||
* @type {boolean}
|
||||
*/
|
||||
verticalMoveLock: false,
|
||||
|
||||
/**
|
||||
* @type {clay.Timeline}
|
||||
*/
|
||||
timeline: null,
|
||||
|
||||
_moveForward: false,
|
||||
_moveBackward: false,
|
||||
_moveLeft: false,
|
||||
_moveRight: false,
|
||||
|
||||
_offsetPitch: 0,
|
||||
_offsetRoll: 0
|
||||
};
|
||||
},
|
||||
function () {
|
||||
this._lockChange = this._lockChange.bind(this);
|
||||
this._keyDown = this._keyDown.bind(this);
|
||||
this._keyUp = this._keyUp.bind(this);
|
||||
@ -80,184 +91,178 @@ const FreeControl = Base.extend(
|
||||
if (this.domElement) {
|
||||
this.init();
|
||||
}
|
||||
},
|
||||
/** @lends clay.plugin.FreeControl.prototype */
|
||||
{
|
||||
/**
|
||||
* init control
|
||||
*/
|
||||
init: function () {
|
||||
// Use pointer lock
|
||||
// http://www.html5rocks.com/en/tutorials/pointerlock/intro/
|
||||
const el = this.domElement;
|
||||
}
|
||||
/**
|
||||
* init control
|
||||
*/
|
||||
init() {
|
||||
// Use pointer lock
|
||||
// http://www.html5rocks.com/en/tutorials/pointerlock/intro/
|
||||
const el = this.domElement;
|
||||
|
||||
//Must request pointer lock after click event, can't not do it directly
|
||||
//Why ? ?
|
||||
vendor.addEventListener(el, 'click', this._requestPointerLock);
|
||||
//Must request pointer lock after click event, can't not do it directly
|
||||
//Why ? ?
|
||||
addEvent(el, 'click', this._requestPointerLock);
|
||||
|
||||
vendor.addEventListener(doc, 'pointerlockchange', this._lockChange);
|
||||
vendor.addEventListener(doc, 'mozpointerlockchange', this._lockChange);
|
||||
vendor.addEventListener(doc, 'webkitpointerlockchange', this._lockChange);
|
||||
addEvent(doc, 'pointerlockchange', this._lockChange);
|
||||
addEvent(doc, 'mozpointerlockchange', this._lockChange);
|
||||
addEvent(doc, 'webkitpointerlockchange', this._lockChange);
|
||||
|
||||
vendor.addEventListener(doc, 'keydown', this._keyDown);
|
||||
vendor.addEventListener(doc, 'keyup', this._keyUp);
|
||||
addEvent(doc, 'keydown', this._keyDown);
|
||||
addEvent(doc, 'keyup', this._keyUp);
|
||||
|
||||
if (this.timeline) {
|
||||
this.timeline.on('frame', this._detectMovementChange, this);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Dispose control
|
||||
*/
|
||||
dispose: function () {
|
||||
const el = this.domElement;
|
||||
|
||||
el.exitPointerLock = el.exitPointerLock || el.mozExitPointerLock || el.webkitExitPointerLock;
|
||||
|
||||
if (el.exitPointerLock) {
|
||||
el.exitPointerLock();
|
||||
}
|
||||
|
||||
vendor.removeEventListener(el, 'click', this._requestPointerLock);
|
||||
|
||||
vendor.removeEventListener(doc, 'pointerlockchange', this._lockChange);
|
||||
vendor.removeEventListener(doc, 'mozpointerlockchange', this._lockChange);
|
||||
vendor.removeEventListener(doc, 'webkitpointerlockchange', this._lockChange);
|
||||
|
||||
vendor.removeEventListener(doc, 'keydown', this._keyDown);
|
||||
vendor.removeEventListener(doc, 'keyup', this._keyUp);
|
||||
|
||||
if (this.timeline) {
|
||||
this.timeline.off('frame', this._detectMovementChange);
|
||||
}
|
||||
},
|
||||
|
||||
_requestPointerLock: function () {
|
||||
const el = this;
|
||||
el.requestPointerLock =
|
||||
el.requestPointerLock || el.mozRequestPointerLock || el.webkitRequestPointerLock;
|
||||
|
||||
el.requestPointerLock();
|
||||
},
|
||||
|
||||
/**
|
||||
* Control update. Should be invoked every frame
|
||||
* @param {number} frameTime Frame time
|
||||
*/
|
||||
update: function (frameTime) {
|
||||
const target = this.target;
|
||||
|
||||
const position = this.target.position;
|
||||
let xAxis = target.localTransform.x.normalize();
|
||||
const zAxis = target.localTransform.z.normalize();
|
||||
|
||||
if (this.verticalMoveLock) {
|
||||
zAxis.y = 0;
|
||||
zAxis.normalize();
|
||||
}
|
||||
|
||||
const speed = (this.speed * frameTime) / 20;
|
||||
|
||||
if (this._moveForward) {
|
||||
// Opposite direction of z
|
||||
position.scaleAndAdd(zAxis, -speed);
|
||||
}
|
||||
if (this._moveBackward) {
|
||||
position.scaleAndAdd(zAxis, speed);
|
||||
}
|
||||
if (this._moveLeft) {
|
||||
position.scaleAndAdd(xAxis, -speed / 2);
|
||||
}
|
||||
if (this._moveRight) {
|
||||
position.scaleAndAdd(xAxis, speed / 2);
|
||||
}
|
||||
|
||||
target.rotateAround(
|
||||
target.position,
|
||||
this.up,
|
||||
(-this._offsetPitch * frameTime * Math.PI) / 360
|
||||
);
|
||||
xAxis = target.localTransform.x;
|
||||
target.rotateAround(target.position, xAxis, (-this._offsetRoll * frameTime * Math.PI) / 360);
|
||||
|
||||
this._offsetRoll = this._offsetPitch = 0;
|
||||
},
|
||||
|
||||
_lockChange: function () {
|
||||
if (
|
||||
doc.pointerLockElement === this.domElement ||
|
||||
doc.mozPointerLockElement === this.domElement ||
|
||||
doc.webkitPointerLockElement === this.domElement
|
||||
) {
|
||||
vendor.addEventListener(doc, 'mousemove', this._mouseMove, false);
|
||||
} else {
|
||||
vendor.removeEventListener(doc, 'mousemove', this._mouseMove);
|
||||
}
|
||||
},
|
||||
|
||||
_mouseMove: function (e) {
|
||||
const dx = e.movementX || e.mozMovementX || e.webkitMovementX || 0;
|
||||
const dy = e.movementY || e.mozMovementY || e.webkitMovementY || 0;
|
||||
|
||||
this._offsetPitch += (dx * this.sensitivity) / 200;
|
||||
this._offsetRoll += (dy * this.sensitivity) / 200;
|
||||
|
||||
// Trigger change event to remind renderer do render
|
||||
this.trigger('change');
|
||||
},
|
||||
|
||||
_detectMovementChange: function (frameTime) {
|
||||
if (this._moveForward || this._moveBackward || this._moveLeft || this._moveRight) {
|
||||
this.trigger('change');
|
||||
}
|
||||
this.update(frameTime);
|
||||
},
|
||||
|
||||
_keyDown: function (e) {
|
||||
switch (e.keyCode) {
|
||||
case 87: //w
|
||||
case 38: //up arrow
|
||||
this._moveForward = true;
|
||||
break;
|
||||
case 83: //s
|
||||
case 40: //down arrow
|
||||
this._moveBackward = true;
|
||||
break;
|
||||
case 65: //a
|
||||
case 37: //left arrow
|
||||
this._moveLeft = true;
|
||||
break;
|
||||
case 68: //d
|
||||
case 39: //right arrow
|
||||
this._moveRight = true;
|
||||
break;
|
||||
}
|
||||
// Trigger change event to remind renderer do render
|
||||
this.trigger('change');
|
||||
},
|
||||
|
||||
_keyUp: function (e) {
|
||||
switch (e.keyCode) {
|
||||
case 87: //w
|
||||
case 38: //up arrow
|
||||
this._moveForward = false;
|
||||
break;
|
||||
case 83: //s
|
||||
case 40: //down arrow
|
||||
this._moveBackward = false;
|
||||
break;
|
||||
case 65: //a
|
||||
case 37: //left arrow
|
||||
this._moveLeft = false;
|
||||
break;
|
||||
case 68: //d
|
||||
case 39: //right arrow
|
||||
this._moveRight = false;
|
||||
break;
|
||||
}
|
||||
if (this.timeline) {
|
||||
this.timeline.on('frame', this._detectMovementChange, this);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Dispose control
|
||||
*/
|
||||
dispose() {
|
||||
const el = this.domElement;
|
||||
|
||||
doc.exitPointerLock = doc.exitPointerLock || (doc as any).mozExitPointerLock;
|
||||
|
||||
if (doc.exitPointerLock) {
|
||||
doc.exitPointerLock();
|
||||
}
|
||||
|
||||
removeEvent(el, 'click', this._requestPointerLock);
|
||||
|
||||
removeEvent(doc, 'pointerlockchange', this._lockChange);
|
||||
removeEvent(doc, 'mozpointerlockchange', this._lockChange);
|
||||
removeEvent(doc, 'webkitpointerlockchange', this._lockChange);
|
||||
|
||||
removeEvent(doc, 'keydown', this._keyDown);
|
||||
removeEvent(doc, 'keyup', this._keyUp);
|
||||
|
||||
if (this.timeline) {
|
||||
this.timeline.off('frame', this._detectMovementChange);
|
||||
}
|
||||
}
|
||||
|
||||
_requestPointerLock() {
|
||||
const el = this.domElement;
|
||||
el.requestPointerLock = el.requestPointerLock || (el as any).mozRequestPointerLock;
|
||||
|
||||
el.requestPointerLock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Control update. Should be invoked every frame
|
||||
* @param {number} frameTime Frame time
|
||||
*/
|
||||
update(frameTime: number) {
|
||||
const target = this.target;
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
const position = target.position;
|
||||
let xAxis = target.localTransform.x.normalize();
|
||||
const zAxis = target.localTransform.z.normalize();
|
||||
|
||||
if (this.verticalMoveLock) {
|
||||
zAxis.y = 0;
|
||||
zAxis.normalize();
|
||||
}
|
||||
|
||||
const speed = (this.speed * frameTime) / 20;
|
||||
|
||||
if (this._moveForward) {
|
||||
// Opposite direction of z
|
||||
position.scaleAndAdd(zAxis, -speed);
|
||||
}
|
||||
if (this._moveBackward) {
|
||||
position.scaleAndAdd(zAxis, speed);
|
||||
}
|
||||
if (this._moveLeft) {
|
||||
position.scaleAndAdd(xAxis, -speed / 2);
|
||||
}
|
||||
if (this._moveRight) {
|
||||
position.scaleAndAdd(xAxis, speed / 2);
|
||||
}
|
||||
|
||||
target.rotateAround(target.position, this.up, (-this._offsetPitch * frameTime * Math.PI) / 360);
|
||||
xAxis = target.localTransform.x;
|
||||
target.rotateAround(target.position, xAxis, (-this._offsetRoll * frameTime * Math.PI) / 360);
|
||||
|
||||
this._offsetRoll = this._offsetPitch = 0;
|
||||
}
|
||||
|
||||
_lockChange() {
|
||||
if (
|
||||
doc.pointerLockElement === this.domElement ||
|
||||
(doc as any).mozPointerLockElement === this.domElement
|
||||
) {
|
||||
addEvent(doc, 'mousemove', this._mouseMove, false);
|
||||
} else {
|
||||
removeEvent(doc, 'mousemove', this._mouseMove);
|
||||
}
|
||||
}
|
||||
|
||||
_mouseMove(e: MouseEvent) {
|
||||
const dx = e.movementX || (e as any).mozMovementX || 0;
|
||||
const dy = e.movementY || (e as any).mozMovementY || 0;
|
||||
|
||||
this._offsetPitch += (dx * this.sensitivity) / 200;
|
||||
this._offsetRoll += (dy * this.sensitivity) / 200;
|
||||
|
||||
// Trigger change event to remind renderer do render
|
||||
this.trigger('change');
|
||||
}
|
||||
|
||||
_detectMovementChange(frameTime: number) {
|
||||
if (this._moveForward || this._moveBackward || this._moveLeft || this._moveRight) {
|
||||
this.trigger('change');
|
||||
}
|
||||
this.update(frameTime);
|
||||
}
|
||||
|
||||
_keyDown(e: KeyboardEvent) {
|
||||
switch (e.keyCode) {
|
||||
case 87: //w
|
||||
case 38: //up arrow
|
||||
this._moveForward = true;
|
||||
break;
|
||||
case 83: //s
|
||||
case 40: //down arrow
|
||||
this._moveBackward = true;
|
||||
break;
|
||||
case 65: //a
|
||||
case 37: //left arrow
|
||||
this._moveLeft = true;
|
||||
break;
|
||||
case 68: //d
|
||||
case 39: //right arrow
|
||||
this._moveRight = true;
|
||||
break;
|
||||
}
|
||||
// Trigger change event to remind renderer do render
|
||||
this.trigger('change');
|
||||
}
|
||||
|
||||
_keyUp(e: KeyboardEvent) {
|
||||
switch (e.keyCode) {
|
||||
case 87: //w
|
||||
case 38: //up arrow
|
||||
this._moveForward = false;
|
||||
break;
|
||||
case 83: //s
|
||||
case 40: //down arrow
|
||||
this._moveBackward = false;
|
||||
break;
|
||||
case 65: //a
|
||||
case 37: //left arrow
|
||||
this._moveLeft = false;
|
||||
break;
|
||||
case 68: //d
|
||||
case 39: //right arrow
|
||||
this._moveRight = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default FreeControl;
|
||||
|
||||
@ -1,93 +1,92 @@
|
||||
// @ts-nocheck
|
||||
import Base from '../core/Base';
|
||||
import Vector3 from '../math/Vector3';
|
||||
import vendor from '../core/vendor';
|
||||
import Notifier from '../core/Notifier';
|
||||
import type ClayNode from '../Node';
|
||||
import type Timeline from '../Timeline';
|
||||
|
||||
const addEvent = vendor.addEventListener;
|
||||
const removeEvent = vendor.removeEventListener;
|
||||
|
||||
interface GamepadControlOpts {
|
||||
/*
|
||||
* Scene node to control, mostly it is a camera.
|
||||
*
|
||||
*/
|
||||
target?: ClayNode;
|
||||
|
||||
/**
|
||||
* Move speed.
|
||||
*/
|
||||
moveSpeed: number;
|
||||
|
||||
/**
|
||||
* Look around speed.
|
||||
*/
|
||||
lookAroundSpeed: number;
|
||||
|
||||
/**
|
||||
* Up axis.
|
||||
*/
|
||||
up: Vector3;
|
||||
|
||||
/**
|
||||
* Timeline.
|
||||
*/
|
||||
timeline?: Timeline;
|
||||
|
||||
/**
|
||||
* Function to be called when a standard gamepad is ready to use.
|
||||
*/
|
||||
onStandardGamepadReady?: (gamepad) => void;
|
||||
|
||||
/**
|
||||
* Function to be called when a gamepad is disconnected.
|
||||
*/
|
||||
onGamepadDisconnected?: (gamepad) => void;
|
||||
}
|
||||
|
||||
interface GamepadControl extends GamepadControlOpts {}
|
||||
/**
|
||||
* Gamepad Control plugin.
|
||||
*
|
||||
* @constructor clay.plugin.GamepadControl
|
||||
*
|
||||
* @example
|
||||
* init: function(app) {
|
||||
* init(app) {
|
||||
* this._gamepadControl = new clay.plugin.GamepadControl({
|
||||
* target: camera,
|
||||
* onStandardGamepadReady: customCallback
|
||||
* });
|
||||
* },
|
||||
*
|
||||
* loop: function(app) {
|
||||
* loop(app) {
|
||||
* this._gamepadControl.update(app.frameTime);
|
||||
* }
|
||||
*/
|
||||
const GamepadControl = Base.extend(
|
||||
function () {
|
||||
return /** @lends clay.plugin.GamepadControl# */ {
|
||||
/**
|
||||
* Scene node to control, mostly it is a camera.
|
||||
*
|
||||
* @type {clay.Node}
|
||||
*/
|
||||
target: null,
|
||||
|
||||
/**
|
||||
* Move speed.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
moveSpeed: 0.1,
|
||||
class GamepadControl extends Notifier {
|
||||
private _moveForward = false;
|
||||
private _moveBackward = false;
|
||||
private _moveLeft = false;
|
||||
private _moveRight = false;
|
||||
|
||||
/**
|
||||
* Look around speed.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
lookAroundSpeed: 0.1,
|
||||
private _offsetPitch = 0;
|
||||
private _offsetRoll = 0;
|
||||
|
||||
/**
|
||||
* Up axis.
|
||||
*
|
||||
* @type {clay.Vector3}
|
||||
*/
|
||||
up: new Vector3(0, 1, 0),
|
||||
private _standardGamepadIndex = 0;
|
||||
private _standardGamepadAvailable = false;
|
||||
private _gamepadAxisThreshold = 0.3;
|
||||
|
||||
/**
|
||||
* Timeline.
|
||||
*
|
||||
* @type {clay.Timeline}
|
||||
*/
|
||||
timeline: null,
|
||||
constructor(opts?: Partial<GamepadControlOpts>) {
|
||||
super();
|
||||
Object.assign(
|
||||
this,
|
||||
{
|
||||
moveSpeed: 0.1,
|
||||
lookAroundSpeed: 0.1,
|
||||
up: Vector3.UP
|
||||
},
|
||||
opts
|
||||
);
|
||||
|
||||
/**
|
||||
* Function to be called when a standard gamepad is ready to use.
|
||||
*
|
||||
* @type {function}
|
||||
*/
|
||||
onStandardGamepadReady: function (gamepad) {},
|
||||
|
||||
/**
|
||||
* Function to be called when a gamepad is disconnected.
|
||||
*
|
||||
* @type {function}
|
||||
*/
|
||||
onGamepadDisconnected: function (gamepad) {},
|
||||
|
||||
// Private properties:
|
||||
|
||||
_moveForward: false,
|
||||
_moveBackward: false,
|
||||
_moveLeft: false,
|
||||
_moveRight: false,
|
||||
|
||||
_offsetPitch: 0,
|
||||
_offsetRoll: 0,
|
||||
|
||||
_connectedGamepadIndex: 0,
|
||||
_standardGamepadAvailable: false,
|
||||
_gamepadAxisThreshold: 0.3
|
||||
};
|
||||
},
|
||||
function () {
|
||||
this._checkGamepadCompatibility = this._checkGamepadCompatibility.bind(this);
|
||||
this._disconnectGamepad = this._disconnectGamepad.bind(this);
|
||||
this._getStandardGamepad = this._getStandardGamepad.bind(this);
|
||||
@ -100,203 +99,207 @@ const GamepadControl = Base.extend(
|
||||
if (typeof navigator.getGamepads === 'function') {
|
||||
this.init();
|
||||
}
|
||||
},
|
||||
/** @lends clay.plugin.GamepadControl.prototype */
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Init. control.
|
||||
*/
|
||||
init() {
|
||||
/**
|
||||
* Init. control.
|
||||
*/
|
||||
init: function () {
|
||||
/**
|
||||
* When user begins to interact with connected gamepad:
|
||||
*
|
||||
* @see https://w3c.github.io/gamepad/#dom-gamepadevent
|
||||
*/
|
||||
vendor.addEventListener(window, 'gamepadconnected', this._checkGamepadCompatibility);
|
||||
|
||||
if (this.timeline) {
|
||||
this.timeline.on('frame', this.update);
|
||||
}
|
||||
|
||||
vendor.addEventListener(window, 'gamepaddisconnected', this._disconnectGamepad);
|
||||
},
|
||||
|
||||
/**
|
||||
* Dispose control.
|
||||
*/
|
||||
dispose: function () {
|
||||
vendor.removeEventListener(window, 'gamepadconnected', this._checkGamepadCompatibility);
|
||||
|
||||
if (this.timeline) {
|
||||
this.timeline.off('frame', this.update);
|
||||
}
|
||||
|
||||
vendor.removeEventListener(window, 'gamepaddisconnected', this._disconnectGamepad);
|
||||
},
|
||||
|
||||
/**
|
||||
* Control's update. Should be invoked every frame.
|
||||
* When user begins to interact with connected gamepad:
|
||||
*
|
||||
* @param {number} frameTime Frame time.
|
||||
* @see https://w3c.github.io/gamepad/#dom-gamepadevent
|
||||
*/
|
||||
update: function (frameTime) {
|
||||
if (!this._standardGamepadAvailable) {
|
||||
return;
|
||||
}
|
||||
addEvent(window, 'gamepadconnected', this._checkGamepadCompatibility);
|
||||
|
||||
this._scanPressedGamepadButtons();
|
||||
this._scanInclinedGamepadAxes();
|
||||
if (this.timeline) {
|
||||
this.timeline.on('frame', this.update);
|
||||
}
|
||||
|
||||
// Update target depending on user input.
|
||||
addEvent(window, 'gamepaddisconnected', this._disconnectGamepad);
|
||||
}
|
||||
|
||||
const target = this.target;
|
||||
/**
|
||||
* Dispose control.
|
||||
*/
|
||||
dispose() {
|
||||
removeEvent(window, 'gamepadconnected', this._checkGamepadCompatibility);
|
||||
|
||||
const position = this.target.position;
|
||||
let xAxis = target.localTransform.x.normalize();
|
||||
const zAxis = target.localTransform.z.normalize();
|
||||
if (this.timeline) {
|
||||
this.timeline.off('frame', this.update);
|
||||
}
|
||||
|
||||
const moveSpeed = (this.moveSpeed * frameTime) / 20;
|
||||
removeEvent(window, 'gamepaddisconnected', this._disconnectGamepad);
|
||||
}
|
||||
|
||||
if (this._moveForward) {
|
||||
// Opposite direction of z.
|
||||
position.scaleAndAdd(zAxis, -moveSpeed);
|
||||
}
|
||||
if (this._moveBackward) {
|
||||
position.scaleAndAdd(zAxis, moveSpeed);
|
||||
}
|
||||
if (this._moveLeft) {
|
||||
position.scaleAndAdd(xAxis, -moveSpeed);
|
||||
}
|
||||
if (this._moveRight) {
|
||||
position.scaleAndAdd(xAxis, moveSpeed);
|
||||
}
|
||||
/**
|
||||
* Control's update. Should be invoked every frame.
|
||||
*
|
||||
* @param {number} frameTime Frame time.
|
||||
*/
|
||||
update(frameTime: number) {
|
||||
if (!this._standardGamepadAvailable) {
|
||||
return;
|
||||
}
|
||||
|
||||
target.rotateAround(
|
||||
target.position,
|
||||
this.up,
|
||||
(-this._offsetPitch * frameTime * Math.PI) / 360
|
||||
);
|
||||
xAxis = target.localTransform.x;
|
||||
target.rotateAround(target.position, xAxis, (-this._offsetRoll * frameTime * Math.PI) / 360);
|
||||
this._scanPressedGamepadButtons();
|
||||
this._scanInclinedGamepadAxes();
|
||||
|
||||
/*
|
||||
* If necessary: trigger `update` event.
|
||||
* XXX This can economize rendering OPs.
|
||||
*/
|
||||
if (
|
||||
this._moveForward === true ||
|
||||
this._moveBackward === true ||
|
||||
this._moveLeft === true ||
|
||||
this._moveRight === true ||
|
||||
this._offsetPitch !== 0 ||
|
||||
this._offsetRoll !== 0
|
||||
) {
|
||||
this.trigger('update');
|
||||
}
|
||||
// Update target depending on user input.
|
||||
|
||||
// Reset values to avoid lost of control.
|
||||
const target = this.target;
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._moveForward = this._moveBackward = this._moveLeft = this._moveRight = false;
|
||||
this._offsetPitch = this._offsetRoll = 0;
|
||||
},
|
||||
const position = target.position;
|
||||
let xAxis = target.localTransform.x.normalize();
|
||||
const zAxis = target.localTransform.z.normalize();
|
||||
|
||||
// Private methods:
|
||||
const moveSpeed = (this.moveSpeed * frameTime) / 20;
|
||||
|
||||
_checkGamepadCompatibility: function (event) {
|
||||
/**
|
||||
* If connected gamepad has a **standard** layout:
|
||||
*
|
||||
* @see https://w3c.github.io/gamepad/#remapping about standard.
|
||||
*/
|
||||
if (event.gamepad.mapping === 'standard') {
|
||||
this._standardGamepadIndex = event.gamepad.index;
|
||||
this._standardGamepadAvailable = true;
|
||||
if (this._moveForward) {
|
||||
// Opposite direction of z.
|
||||
position.scaleAndAdd(zAxis, -moveSpeed);
|
||||
}
|
||||
if (this._moveBackward) {
|
||||
position.scaleAndAdd(zAxis, moveSpeed);
|
||||
}
|
||||
if (this._moveLeft) {
|
||||
position.scaleAndAdd(xAxis, -moveSpeed);
|
||||
}
|
||||
if (this._moveRight) {
|
||||
position.scaleAndAdd(xAxis, moveSpeed);
|
||||
}
|
||||
|
||||
this.onStandardGamepadReady(event.gamepad);
|
||||
}
|
||||
},
|
||||
target.rotateAround(target.position, this.up, (-this._offsetPitch * frameTime * Math.PI) / 360);
|
||||
xAxis = target.localTransform.x;
|
||||
target.rotateAround(target.position, xAxis, (-this._offsetRoll * frameTime * Math.PI) / 360);
|
||||
|
||||
_disconnectGamepad: function (event) {
|
||||
this._standardGamepadAvailable = false;
|
||||
/*
|
||||
* If necessary: trigger `update` event.
|
||||
* XXX This can economize rendering OPs.
|
||||
*/
|
||||
if (
|
||||
this._moveForward === true ||
|
||||
this._moveBackward === true ||
|
||||
this._moveLeft === true ||
|
||||
this._moveRight === true ||
|
||||
this._offsetPitch !== 0 ||
|
||||
this._offsetRoll !== 0
|
||||
) {
|
||||
this.trigger('update');
|
||||
}
|
||||
|
||||
this.onGamepadDisconnected(event.gamepad);
|
||||
},
|
||||
// Reset values to avoid lost of control.
|
||||
|
||||
_getStandardGamepad: function () {
|
||||
return navigator.getGamepads()[this._standardGamepadIndex];
|
||||
},
|
||||
this._moveForward = this._moveBackward = this._moveLeft = this._moveRight = false;
|
||||
this._offsetPitch = this._offsetRoll = 0;
|
||||
}
|
||||
|
||||
_scanPressedGamepadButtons: function () {
|
||||
const gamepadButtons = this._getStandardGamepad().buttons;
|
||||
// Private methods:
|
||||
|
||||
// For each gamepad button:
|
||||
for (let gamepadButtonId = 0; gamepadButtonId < gamepadButtons.length; gamepadButtonId++) {
|
||||
// Get user input.
|
||||
const gamepadButton = gamepadButtons[gamepadButtonId];
|
||||
_checkGamepadCompatibility(event: GamepadEvent) {
|
||||
/**
|
||||
* If connected gamepad has a **standard** layout:
|
||||
*
|
||||
* @see https://w3c.github.io/gamepad/#remapping about standard.
|
||||
*/
|
||||
if (event.gamepad.mapping === 'standard') {
|
||||
this._standardGamepadIndex = event.gamepad.index;
|
||||
this._standardGamepadAvailable = true;
|
||||
|
||||
if (gamepadButton.pressed) {
|
||||
switch (gamepadButtonId) {
|
||||
// D-pad Up
|
||||
case 12:
|
||||
this._moveForward = true;
|
||||
break;
|
||||
this.onStandardGamepadReady && this.onStandardGamepadReady(event.gamepad);
|
||||
}
|
||||
}
|
||||
|
||||
// D-pad Down
|
||||
case 13:
|
||||
this._moveBackward = true;
|
||||
break;
|
||||
_disconnectGamepad(event: GamepadEvent) {
|
||||
this._standardGamepadAvailable = false;
|
||||
|
||||
// D-pad Left
|
||||
case 14:
|
||||
this._moveLeft = true;
|
||||
break;
|
||||
this.onGamepadDisconnected && this.onGamepadDisconnected(event.gamepad);
|
||||
}
|
||||
|
||||
// D-pad Right
|
||||
case 15:
|
||||
this._moveRight = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
_getStandardGamepad() {
|
||||
return navigator.getGamepads()[this._standardGamepadIndex];
|
||||
}
|
||||
|
||||
_scanInclinedGamepadAxes: function () {
|
||||
const gamepadAxes = this._getStandardGamepad().axes;
|
||||
_scanPressedGamepadButtons() {
|
||||
const gamepad = this._getStandardGamepad();
|
||||
const gamepadButtons = gamepad && gamepad.buttons;
|
||||
if (!gamepadButtons) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For each gamepad axis:
|
||||
for (let gamepadAxisId = 0; gamepadAxisId < gamepadAxes.length; gamepadAxisId++) {
|
||||
// Get user input.
|
||||
const gamepadAxis = gamepadAxes[gamepadAxisId];
|
||||
// For each gamepad button:
|
||||
for (let gamepadButtonId = 0; gamepadButtonId < gamepadButtons.length; gamepadButtonId++) {
|
||||
// Get user input.
|
||||
const gamepadButton = gamepadButtons[gamepadButtonId];
|
||||
|
||||
// XXX We use a threshold because axes are never neutral.
|
||||
if (Math.abs(gamepadAxis) > this._gamepadAxisThreshold) {
|
||||
switch (gamepadAxisId) {
|
||||
// Left stick X±
|
||||
case 0:
|
||||
this._moveLeft = gamepadAxis < 0;
|
||||
this._moveRight = gamepadAxis > 0;
|
||||
break;
|
||||
if (gamepadButton.pressed) {
|
||||
switch (gamepadButtonId) {
|
||||
// D-pad Up
|
||||
case 12:
|
||||
this._moveForward = true;
|
||||
break;
|
||||
|
||||
// Left stick Y±
|
||||
case 1:
|
||||
this._moveForward = gamepadAxis < 0;
|
||||
this._moveBackward = gamepadAxis > 0;
|
||||
break;
|
||||
// D-pad Down
|
||||
case 13:
|
||||
this._moveBackward = true;
|
||||
break;
|
||||
|
||||
// Right stick X±
|
||||
case 2:
|
||||
this._offsetPitch += gamepadAxis * this.lookAroundSpeed;
|
||||
break;
|
||||
// D-pad Left
|
||||
case 14:
|
||||
this._moveLeft = true;
|
||||
break;
|
||||
|
||||
// Right stick Y±
|
||||
case 3:
|
||||
this._offsetRoll += gamepadAxis * this.lookAroundSpeed;
|
||||
break;
|
||||
}
|
||||
// D-pad Right
|
||||
case 15:
|
||||
this._moveRight = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
_scanInclinedGamepadAxes() {
|
||||
const gamepad = this._getStandardGamepad();
|
||||
const gamepadAxes = gamepad && gamepad.axes;
|
||||
if (!gamepadAxes) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For each gamepad axis:
|
||||
for (let gamepadAxisId = 0; gamepadAxisId < gamepadAxes.length; gamepadAxisId++) {
|
||||
// Get user input.
|
||||
const gamepadAxis = gamepadAxes[gamepadAxisId];
|
||||
|
||||
// XXX We use a threshold because axes are never neutral.
|
||||
if (Math.abs(gamepadAxis) > this._gamepadAxisThreshold) {
|
||||
switch (gamepadAxisId) {
|
||||
// Left stick X±
|
||||
case 0:
|
||||
this._moveLeft = gamepadAxis < 0;
|
||||
this._moveRight = gamepadAxis > 0;
|
||||
break;
|
||||
|
||||
// Left stick Y±
|
||||
case 1:
|
||||
this._moveForward = gamepadAxis < 0;
|
||||
this._moveBackward = gamepadAxis > 0;
|
||||
break;
|
||||
|
||||
// Right stick X±
|
||||
case 2:
|
||||
this._offsetPitch += gamepadAxis * this.lookAroundSpeed;
|
||||
break;
|
||||
|
||||
// Right stick Y±
|
||||
case 3:
|
||||
this._offsetRoll += gamepadAxis * this.lookAroundSpeed;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
export default GamepadControl;
|
||||
|
||||
@ -1,31 +1,42 @@
|
||||
// @ts-nocheck
|
||||
import * as util from '../core/util';
|
||||
|
||||
const GestureMgr = function () {
|
||||
this._track = [];
|
||||
};
|
||||
type Target = any;
|
||||
interface TrackItem {
|
||||
points: number[][];
|
||||
touches: Touch[];
|
||||
target: Target;
|
||||
event: TouchEvent;
|
||||
}
|
||||
|
||||
GestureMgr.prototype = {
|
||||
constructor: GestureMgr,
|
||||
export interface PinchEvent extends TouchEvent {
|
||||
pinchScale: number;
|
||||
pinchX: number;
|
||||
pinchY: number;
|
||||
}
|
||||
|
||||
recognize: function (event, target, root) {
|
||||
export class GestureMgr {
|
||||
private _track: TrackItem[] = [];
|
||||
|
||||
constructor() {}
|
||||
|
||||
recognize(event: TouchEvent, target: Target, root: HTMLElement) {
|
||||
this._doTrack(event, target, root);
|
||||
return this._recognize(event);
|
||||
},
|
||||
}
|
||||
|
||||
clear: function () {
|
||||
clear() {
|
||||
this._track.length = 0;
|
||||
return this;
|
||||
},
|
||||
}
|
||||
|
||||
_doTrack: function (event, target, root) {
|
||||
const touches = event.targetTouches;
|
||||
_doTrack(event: TouchEvent, target: Target, root: HTMLElement) {
|
||||
const touches = event.touches;
|
||||
|
||||
if (!touches) {
|
||||
return;
|
||||
}
|
||||
|
||||
const trackItem = {
|
||||
const trackItem: TrackItem = {
|
||||
points: [],
|
||||
touches: [],
|
||||
target: target,
|
||||
@ -39,9 +50,9 @@ GestureMgr.prototype = {
|
||||
}
|
||||
|
||||
this._track.push(trackItem);
|
||||
},
|
||||
}
|
||||
|
||||
_recognize: function (event) {
|
||||
_recognize(event: TouchEvent) {
|
||||
for (const eventName in recognizers) {
|
||||
if (util.hasOwn(recognizers, eventName)) {
|
||||
const gestureInfo = recognizers[eventName](this._track, event);
|
||||
@ -51,47 +62,59 @@ GestureMgr.prototype = {
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function dist(pointPair) {
|
||||
function dist(pointPair: number[][]): number {
|
||||
const dx = pointPair[1][0] - pointPair[0][0];
|
||||
const dy = pointPair[1][1] - pointPair[0][1];
|
||||
|
||||
return Math.sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
function center(pointPair) {
|
||||
function center(pointPair: number[][]): number[] {
|
||||
return [(pointPair[0][0] + pointPair[1][0]) / 2, (pointPair[0][1] + pointPair[1][1]) / 2];
|
||||
}
|
||||
|
||||
const recognizers = {
|
||||
pinch: function (track, event) {
|
||||
const trackLen = track.length;
|
||||
type Recognizer = (
|
||||
tracks: TrackItem[],
|
||||
event: TouchEvent
|
||||
) =>
|
||||
| {
|
||||
type: 'pinch';
|
||||
target: Target;
|
||||
event: PinchEvent;
|
||||
}
|
||||
| undefined;
|
||||
|
||||
const recognizers: Record<string, Recognizer> = {
|
||||
pinch(tracks: TrackItem[], event: TouchEvent) {
|
||||
const trackLen = tracks.length;
|
||||
|
||||
if (!trackLen) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pinchEnd = (track[trackLen - 1] || {}).points;
|
||||
const pinchPre = (track[trackLen - 2] || {}).points || pinchEnd;
|
||||
const pinchEnd = (tracks[trackLen - 1] || {}).points;
|
||||
const pinchPre = (tracks[trackLen - 2] || {}).points || pinchEnd;
|
||||
|
||||
if (pinchPre && pinchPre.length > 1 && pinchEnd && pinchEnd.length > 1) {
|
||||
let pinchScale = dist(pinchEnd) / dist(pinchPre);
|
||||
!isFinite(pinchScale) && (pinchScale = 1);
|
||||
|
||||
event.pinchScale = pinchScale;
|
||||
(event as PinchEvent).pinchScale = pinchScale;
|
||||
|
||||
const pinchCenter = center(pinchEnd);
|
||||
event.pinchX = pinchCenter[0];
|
||||
event.pinchY = pinchCenter[1];
|
||||
(event as PinchEvent).pinchX = pinchCenter[0];
|
||||
(event as PinchEvent).pinchY = pinchCenter[1];
|
||||
|
||||
return {
|
||||
type: 'pinch',
|
||||
target: track[0].target,
|
||||
event: event
|
||||
target: tracks[0].target,
|
||||
event: event as PinchEvent
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Only pinch currently.
|
||||
};
|
||||
export default GestureMgr;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,3 @@
|
||||
// @ts-nocheck
|
||||
import Skybox from './Skybox';
|
||||
|
||||
export default Skybox;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user