UNPKG

@polygonjs/polygonjs

Version:

node-based WebGL 3D engine https://polygonjs.com

321 lines (320 loc) 10.8 kB
"use strict"; import { CapsuleSopOperation } from "./../../operations/sop/Capsule"; import { TypedCameraControlsEventNode } from "./_BaseCameraControls"; import { NodeParamsConfig, ParamConfig } from "../utils/params/ParamsConfig"; import { EventConnectionPoint, EventConnectionPointType } from "../utils/io/connections/Event"; import { PointerLockControls } from "../../../modules/core/controls/PointerLockControls"; import { CameraControlsNodeType, NodeContext } from "../../poly/NodeContext"; import { CorePlayer } from "../../../core/player/Player"; import { isBooleanTrue } from "../../../core/BooleanValue"; import { CollisionController } from "./collision/CollisionController"; const EVENT_LOCK = "lock"; const EVENT_CHANGE = "change"; const EVENT_UNLOCK = "unlock"; function updatePlayerParamsCallbackOption() { return { cook: false, callback: (node) => { FirstPersonControlsEventNode.PARAM_CALLBACK_updatePlayerParams(node); } }; } class FirstPersonEventParamsConfig 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 FirstPersonControls 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) => { FirstPersonControlsEventNode.PARAM_CALLBACK_updateCollider(node); } }); /** @param click to lock controls */ this.lock = ParamConfig.BUTTON(null, { callback: (node) => { FirstPersonControlsEventNode.PARAM_CALLBACK_lockControls(node); } }); /** @param click to unlock controls */ this.unlock = ParamConfig.BUTTON(null, { callback: (node) => { FirstPersonControlsEventNode.PARAM_CALLBACK_unlockControls(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 translate speed */ this.translateSpeed = ParamConfig.FLOAT(1, { range: [0, 10], rangeLocked: [true, false], ...updatePlayerParamsCallbackOption() }); /** @param rotate speed */ this.rotateSpeed = ParamConfig.FLOAT(1, { range: [0, 10], rangeLocked: [true, false] // ...updatePlayerParamsCallbackOption(), }); /** @param jump Allowed */ this.jumpAllowed = ParamConfig.BOOLEAN(true, { ...updatePlayerParamsCallbackOption() }); /** @param jump Force */ this.jumpStrength = ParamConfig.FLOAT(10, { range: [0, 100], rangeLocked: [true, false], visibleIf: { jumpAllowed: 1 }, ...updatePlayerParamsCallbackOption() }); /** @param run Allowed */ this.runAllowed = ParamConfig.BOOLEAN(true, { ...updatePlayerParamsCallbackOption() }); /** @param run speed mult */ this.runSpeedMult = ParamConfig.FLOAT(2, { range: [0, 10], rangeLocked: [true, false], visibleIf: { runAllowed: 1 }, ...updatePlayerParamsCallbackOption() }); /** @param recompute colliding geo */ this.updateCollider = ParamConfig.BUTTON(null, { callback: (node) => { FirstPersonControlsEventNode.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) => { FirstPersonControlsEventNode.PARAM_CALLBACK_resetPlayer(node); } }); /** @param min rotation angle */ this.minPolarAngle = ParamConfig.FLOAT(0, { range: [0, Math.PI], rangeLocked: [true, true] }); /** @param max rotation angle */ this.maxPolarAngle = ParamConfig.FLOAT("$PI", { range: [0, Math.PI], rangeLocked: [true, true] }); this.html = ParamConfig.FOLDER(); /** @param specify a custom HTML element */ this.customLockCursorElement = ParamConfig.BOOLEAN(false); /** @param jump HTML element selector */ this.lockCursorElementSelector = ParamConfig.STRING("#lock-cursor-element", { visibleIf: { customLockCursorElement: true } }); } } const ParamsConfig = new FirstPersonEventParamsConfig(); const _FirstPersonControlsEventNode = class extends TypedCameraControlsEventNode { constructor() { super(...arguments); this.paramsConfig = ParamsConfig; this._controls_by_element_id = /* @__PURE__ */ new Map(); } static type() { return CameraControlsNodeType.FIRST_PERSON; } endEventName() { return "unlock"; } collisionController() { return this._collisionController = this._collisionController || new CollisionController(this); } initializeNode() { this.io.inputs.setNamedInputConnectionPoints([ new EventConnectionPoint(EVENT_LOCK, EventConnectionPointType.BASE, this.lockControls.bind(this)), new EventConnectionPoint( _FirstPersonControlsEventNode.INPUT_UPDATE_COLLIDER, EventConnectionPointType.BASE, this._updateCollider.bind(this) ), new EventConnectionPoint( _FirstPersonControlsEventNode.INPUT_RESET, EventConnectionPointType.BASE, this._resetPlayer.bind(this) ) ]); this.io.outputs.setNamedOutputConnectionPoints([ new EventConnectionPoint(EVENT_LOCK, EventConnectionPointType.BASE), new EventConnectionPoint(EVENT_CHANGE, EventConnectionPointType.BASE), new EventConnectionPoint(EVENT_UNLOCK, EventConnectionPointType.BASE) ]); } async createControlsInstance(camera, element) { await this._initPlayer(camera); const _getLockHTMLElement = () => { if (!this.pv.customLockCursorElement) { return void 0; } const element2 = document.querySelector(this.pv.lockCursorElementSelector); return element2; }; const lockHTMLElement = _getLockHTMLElement(); const controls = new PointerLockControls(camera, element, { lockHTMLElement }, 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.startRotation.copy(this.pv.startRotation); 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 }); } _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_LOCK, () => { this.dispatchEventToOutput(EVENT_LOCK, {}); }); controls.addEventListener(EVENT_CHANGE, () => { this.dispatchEventToOutput(EVENT_CHANGE, {}); }); controls.addEventListener(EVENT_UNLOCK, () => { this.dispatchEventToOutput(EVENT_UNLOCK, {}); }); } updateRequired() { return true; } setupControls(controls) { controls.minPolarAngle = this.pv.minPolarAngle; controls.maxPolarAngle = this.pv.maxPolarAngle; controls.rotateSpeed = this.pv.rotateSpeed; } disposeControlsForHtmlElementId(htmlElementId) { const controls = this._controls_by_element_id.get(htmlElementId); if (controls) { controls.dispose(); this._controls_by_element_id.delete(htmlElementId); } } unlockControls() { const controls = this._firstControls(); if (!controls) { return; } controls.unlock(); } // // // LOCK // // lockControls() { const controls = this._firstControls(); if (!controls) { return; } controls.lock(); } _firstControls() { let firstControls; this._controls_by_element_id.forEach((controls, id) => { firstControls = firstControls || controls; }); return firstControls; } static PARAM_CALLBACK_lockControls(node) { node.lockControls(); } static PARAM_CALLBACK_unlockControls(node) { node.unlockControls(); } static PARAM_CALLBACK_updateCollider(node) { node._updateCollider(); } static PARAM_CALLBACK_updatePlayerParams(node) { node._updatePlayerParams(); } static PARAM_CALLBACK_resetPlayer(node) { node._resetPlayer(); } }; export let FirstPersonControlsEventNode = _FirstPersonControlsEventNode; FirstPersonControlsEventNode.INPUT_UPDATE_COLLIDER = "updateCollider"; FirstPersonControlsEventNode.INPUT_RESET = "reset";