@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
313 lines (312 loc) • 10.6 kB
JavaScript
"use strict";
import { TypedCameraControlsEventNode } from "./_BaseCameraControls";
import { NodeParamsConfig, ParamConfig } from "../utils/params/ParamsConfig";
import { EventConnectionPoint, EventConnectionPointType } from "../utils/io/connections/Event";
import {
MobileJoystickControls,
DEFAULT_PARAMS
} from "../../../modules/core/controls/MobileJoystickControls";
import { CameraControlsNodeType, NodeContext } from "../../poly/NodeContext";
import { CorePlayer } from "../../../core/player/Player";
import { isBooleanTrue } from "../../../core/Type";
import { CollisionController } from "./collision/CollisionController";
import { CapsuleSopOperation } from "../../operations/sop/Capsule";
const EVENT_START = "start";
const EVENT_CHANGE = "change";
const EVENT_END = "end";
function updatePlayerParamsCallbackOption() {
return {
cook: false,
callback: (node) => {
MobileJoystickControlsEventNode.PARAM_CALLBACK_updatePlayerParams(node);
}
};
}
class MobileJoystickEventParamsConfig extends NodeParamsConfig {
constructor() {
super(...arguments);
this.main = ParamConfig.FOLDER();
/** @param collider object */
this.colliderObject = ParamConfig.NODE_PATH("", {
nodeSelection: {
context: NodeContext.SOP
},
// if the node is dependent,
// the MobileJoystickControlsEventNode will be re-created when this node changes
// which we do not want, as it will act like a hard reset
// when all we want is to update the collider
dependentOnFoundNode: false,
callback: (node) => {
MobileJoystickControlsEventNode.PARAM_CALLBACK_updateCollider(node);
}
});
/** @param collision Capsule Radius */
this.capsuleRadius = ParamConfig.FLOAT(CapsuleSopOperation.DEFAULT_PARAMS.radius, {
range: [0, 1],
rangeLocked: [true, false],
...updatePlayerParamsCallbackOption()
});
/** @param collision Capsule Height */
this.capsuleHeight = ParamConfig.FLOAT(CapsuleSopOperation.DEFAULT_PARAMS.height, {
range: [0, 2],
rangeLocked: [true, false],
...updatePlayerParamsCallbackOption()
});
this.physics = ParamConfig.FOLDER();
/** @param physics Steps */
this.physicsSteps = ParamConfig.INTEGER(5, {
range: [1, 10],
rangeLocked: [true, false],
...updatePlayerParamsCallbackOption()
});
/** @param gravity */
this.gravity = ParamConfig.VECTOR3([0, -30, 0], {
...updatePlayerParamsCallbackOption()
});
/** @param translation speed */
this.translateSpeed = ParamConfig.FLOAT(1);
/** @param rotation speed */
this.rotateSpeed = ParamConfig.FLOAT(DEFAULT_PARAMS.rotateSpeed);
/** @param specify a custom HTML element */
this.customTranslateElement = ParamConfig.BOOLEAN(false, {
separatorBefore: true
});
/** @param jump HTML element selector */
this.translateElementSelector = ParamConfig.STRING("#translate-element", {
visibleIf: {
customTranslateElement: true
}
});
/** @param jump Allowed */
this.jumpAllowed = ParamConfig.BOOLEAN(true, {
separatorBefore: true,
...updatePlayerParamsCallbackOption()
});
/** @param jump Force */
this.jumpStrength = ParamConfig.FLOAT(10, {
range: [0, 100],
rangeLocked: [true, false],
...updatePlayerParamsCallbackOption()
});
/** @param specify a custom HTML element */
this.customJumpElement = ParamConfig.BOOLEAN(false, {
visibleIf: {
jumpAllowed: true
}
});
/** @param jump HTML element selector */
this.jumpElementSelector = ParamConfig.STRING("#jump-element", {
visibleIf: {
jumpAllowed: true,
customJumpElement: true
}
});
/** @param run Allowed */
this.runAllowed = ParamConfig.BOOLEAN(true, {
separatorBefore: true,
...updatePlayerParamsCallbackOption()
});
/** @param run speed mult */
this.runSpeedMult = ParamConfig.FLOAT(2, {
range: [0, 10],
rangeLocked: [true, false],
...updatePlayerParamsCallbackOption()
});
/** @param specify a custom HTML element */
this.customRunElement = ParamConfig.BOOLEAN(false, {
visibleIf: {
runAllowed: true
}
});
/** @param jump HTML element selector */
this.runElementSelector = ParamConfig.STRING("#run-element", {
visibleIf: {
runAllowed: true,
customRunElement: true
}
});
/** @param recompute colliding geo */
this.updateCollider = ParamConfig.BUTTON(null, {
separatorBefore: true,
callback: (node) => {
MobileJoystickControlsEventNode.PARAM_CALLBACK_updateCollider(node);
}
});
this.init = ParamConfig.FOLDER();
/** @param start Position */
this.startPosition = ParamConfig.VECTOR3([0, 2, 0], {
...updatePlayerParamsCallbackOption()
});
/** @param start Position */
this.startRotation = ParamConfig.VECTOR3([0, 0, 0], {
...updatePlayerParamsCallbackOption()
});
/** @param reset */
this.reset = ParamConfig.BUTTON(null, {
callback: (node) => {
MobileJoystickControlsEventNode.PARAM_CALLBACK_resetPlayer(node);
}
});
/** @param min polar angle */
this.minPolarAngle = ParamConfig.FLOAT("-$PI*0.5", {
range: [-Math.PI, Math.PI],
rangeLocked: [true, true]
});
/** @param max polar angle */
this.maxPolarAngle = ParamConfig.FLOAT("$PI*0.5", {
range: [-Math.PI, Math.PI],
rangeLocked: [true, true]
});
}
}
const ParamsConfig = new MobileJoystickEventParamsConfig();
const _MobileJoystickControlsEventNode = class extends TypedCameraControlsEventNode {
constructor() {
super(...arguments);
this.paramsConfig = ParamsConfig;
this._controls_by_element_id = /* @__PURE__ */ new Map();
}
static type() {
return CameraControlsNodeType.MOBILE_JOYSTICK;
}
endEventName() {
return "end";
}
collisionController() {
return this._collisionController = this._collisionController || new CollisionController(this);
}
initializeNode() {
this.io.inputs.setNamedInputConnectionPoints([
new EventConnectionPoint(
_MobileJoystickControlsEventNode.INPUT_UPDATE_COLLIDER,
EventConnectionPointType.BASE,
this._updateCollider.bind(this)
)
]);
this.io.outputs.setNamedOutputConnectionPoints([
new EventConnectionPoint(EVENT_START, EventConnectionPointType.BASE),
new EventConnectionPoint(EVENT_CHANGE, EventConnectionPointType.BASE),
new EventConnectionPoint(EVENT_END, EventConnectionPointType.BASE)
]);
}
async createControlsInstance(camera, element) {
await this._initPlayer(camera);
function _getElement(options2) {
if (!options2.actionAllowed) {
return;
}
if (!options2.customElement) {
return;
}
return document.querySelector(options2.selector) || void 0;
}
const translateDomElement = _getElement({
actionAllowed: true,
customElement: this.pv.customTranslateElement,
selector: this.pv.translateElementSelector
});
const runDomElement = _getElement({
actionAllowed: this.pv.runAllowed,
customElement: this.pv.customRunElement,
selector: this.pv.runElementSelector
});
const jumpDomElement = _getElement({
actionAllowed: this.pv.jumpAllowed,
customElement: this.pv.customJumpElement,
selector: this.pv.jumpElementSelector
});
const options = {
translateDomElement,
runDomElement,
jumpDomElement
};
const controls = new MobileJoystickControls(camera, element, options, this._player);
this._controls_by_element_id.set(element.id, controls);
this._bind_listeners_to_controls_instance(controls);
return controls;
}
async _initPlayer(camera) {
const options = await this._playerOptions(camera);
if (!options) {
return;
}
this._player = this._player || new CorePlayer(options);
this._player.setOptions(options);
this._updatePlayerParams();
this._player.reset();
}
async _playerOptions(camera) {
const collider = await this.collisionController().getCollider();
if (!collider) {
this.states.error.set("invalid collider");
return;
}
return { object: camera, collider };
}
player() {
return this._player;
}
async _updatePlayerParams() {
if (!this._player) {
return;
}
this._player.startPosition.copy(this.pv.startPosition);
this._player.physicsSteps = this.pv.physicsSteps;
this._player.jumpAllowed = isBooleanTrue(this.pv.jumpAllowed);
this._player.jumpStrength = this.pv.jumpStrength;
this._player.runAllowed = isBooleanTrue(this.pv.runAllowed);
this._player.runSpeedMult = this.pv.runSpeedMult;
this._player.gravity.copy(this.pv.gravity);
this._player.speed = this.pv.translateSpeed;
this._player.setCapsule({
radius: this.pv.capsuleRadius,
height: this.pv.capsuleHeight,
divisions: 5,
center: CapsuleSopOperation.DEFAULT_PARAMS.center
});
this._controls_by_element_id.forEach((controls) => controls.updateElements());
}
_resetPlayer() {
var _a;
(_a = this._player) == null ? void 0 : _a.reset();
}
async _updateCollider() {
await this.collisionController().updateCollider();
}
_bind_listeners_to_controls_instance(controls) {
controls.addEventListener(EVENT_START, () => {
this.dispatchEventToOutput(EVENT_START, {});
});
controls.addEventListener(EVENT_CHANGE, () => {
this.dispatchEventToOutput(EVENT_CHANGE, {});
});
controls.addEventListener(EVENT_END, () => {
this.dispatchEventToOutput(EVENT_END, {});
});
}
updateRequired() {
return true;
}
setupControls(controls) {
controls.setRotationSpeed(this.pv.rotateSpeed);
controls.setRotationRange({ min: this.pv.minPolarAngle, max: this.pv.maxPolarAngle });
controls.updateElements();
}
disposeControlsForHtmlElementId(html_element_id) {
const controls = this._controls_by_element_id.get(html_element_id);
if (controls) {
this._controls_by_element_id.delete(html_element_id);
}
}
static PARAM_CALLBACK_updateCollider(node) {
node._updateCollider();
}
static PARAM_CALLBACK_updatePlayerParams(node) {
node._updatePlayerParams();
}
static PARAM_CALLBACK_resetPlayer(node) {
node._resetPlayer();
}
};
export let MobileJoystickControlsEventNode = _MobileJoystickControlsEventNode;
MobileJoystickControlsEventNode.INPUT_UPDATE_COLLIDER = "updateCollider";