UNPKG

@polygonjs/polygonjs

Version:

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

394 lines (375 loc) 13.9 kB
import type {World, RigidBody, Collider, KinematicCharacterController} from '@dimforge/rapier3d-compat'; import {PhysicsLib} from '../CorePhysics'; import {Object3D, Vector3} from 'three'; import {PhysicsPlayerType} from './PhysicsPlayer'; import {createCharacterController} from './CharacterController'; import {_physicsRBDResetAll} from '../PhysicsRBD'; import {CorePhysicsAttribute} from '../PhysicsAttribute'; import {CorePath} from '../../geometry/CorePath'; import {CoreCameraControlsController, ApplicableControlsNode} from '../../camera/CoreCameraControlsController'; import {PolyScene} from '../../../engine/scene/PolyScene'; import {Camera} from 'three'; import {CorePlayerInput} from '../../player/PlayerCommon'; type BooleanGetter = () => boolean; // type SetInputData = (inputData: CorePlayerPhysicsInputData) => void; type UpdateFunc = ( computeData: CorePlayerPhysicsComputeInputData, inputData: CorePlayerPhysicsInputData, delta: number ) => void; // export enum CorePlayerPhysicsInput { // LEFT = 'left', // RIGHT = 'right', // BACKWARD = 'backward', // FORWARD = 'forward', // RUN = 'run', // JUMP = 'jump', // } // export const CORE_PLAYER_PHYSICS_INPUTS: CorePlayerPhysicsInput[] = [ // CorePlayerPhysicsInput.LEFT, // CorePlayerPhysicsInput.RIGHT, // CorePlayerPhysicsInput.BACKWARD, // CorePlayerPhysicsInput.FORWARD, // CorePlayerPhysicsInput.RUN, // CorePlayerPhysicsInput.JUMP, // ]; export type CorePlayerPhysicsInputData = Record<CorePlayerInput, boolean>; export interface CorePlayerPhysicsComputeInputData { speed: number; runAllowed: boolean; runSpeedMult: number; jumpAllowed: boolean; jumpStrength: number; resetIfBelowThreshold: boolean; resetThreshold: number; } interface CorePlayerPhysicsOptions { scene: PolyScene; object: Object3D; PhysicsLib: PhysicsLib; world: World; worldObject: Object3D; body: RigidBody; collider: Collider; type: PhysicsPlayerType; } interface CameraAndControls { camera: Camera; controlsNode: ApplicableControlsNode; } // const gravity = new Vector3(); // const forcesByDelta = new Vector3(); // const desiredTranslation = new Vector3(); // const correctedTranslation = new Vector3(); const torqueLR = new Vector3(); const torqueFB = new Vector3(); const up = new Vector3(0, 1, 0); // we need a default forwardDir, // in case we never give a camera to the player. // otherwise if it is 0,0,0, the player would never move const DEFAULT_FORWARD_DIR = new Vector3(0, 0, -1); const forwardDir = new Vector3().copy(DEFAULT_FORWARD_DIR); const currentTarget = new Vector3(); const delta = new Vector3(); const newCameraPosition = new Vector3(); const newTarget = new Vector3(); const LERP = 0.2; export class CorePlayerPhysics { // private _inputData: CorePlayerPhysicsInputData = { // forward: false, // backward: false, // left: false, // right: false, // run: false, // jump: false, // }; // private _computeInputData: CorePlayerPhysicsComputeInputData = { // speed: 1, // runAllowed: true, // runSpeedMult: 2, // jumpAllowed: true, // jumpStrength: 1, // resetIfBelowThreshold: true, // resetThreshold: -5, // }; // private _mass = 1; private _userTorques = new Vector3(0, 0, 0); private _userImpulses = new Vector3(0, 0, 0); // private _userForces = new Vector3(0, 0, 0); // private _velocity = new Vector3(0, 0, 0); private _correctedMovement = new Vector3(0, 0, 0); // private _computedData: PlayerComputedData = { // velocityFromForces: new Vector3(0, 0, 0), // onGround: false, // velocityFromPositionDelta: new Vector3(0, 0, 0), // }; protected object: Object3D; // protected worldObject: Object3D; protected PhysicsLib: PhysicsLib; protected world: World; protected body: RigidBody; protected collider: Collider; // protected type: PhysicsPlayerType protected characterController: KinematicCharacterController | undefined; protected startPosition: Vector3 = new Vector3(); public onGround: BooleanGetter; // public setInputData: SetInputData; public update: UpdateFunc; private cameraAndControls: CameraAndControls | undefined; constructor(protected options: CorePlayerPhysicsOptions) { this.object = options.object; this.PhysicsLib = options.PhysicsLib; this.world = options.world; this.body = options.body; this.collider = options.collider; this.startPosition.copy(this.object.position); if (options.type == PhysicsPlayerType.CHARACTER_CONTROLLER) { this.characterController = createCharacterController(this.world, this.object); this.onGround = this.onGroundWithCharacterController; // this.setInputData = this.setInputDataWithCharacterController; this.update = this.updateWithCharacterController; } else { this.onGround = this.onGroundWithTorque; // this.setInputData = this.setInputDataWithTorque; this.update = this.updateWithTorque; this.initWithTorque(options.worldObject, options.scene); } } dispose() { if (this.characterController) { this.world.removeCharacterController(this.characterController); } } // setComputeInputData(data: CorePlayerPhysicsComputeInputData) { // this._computeInputData.speed = data.speed; // this._computeInputData.runAllowed = data.runAllowed; // this._computeInputData.runSpeedMult = data.runSpeedMult; // this._computeInputData.jumpAllowed = data.jumpAllowed; // this._computeInputData.jumpStrength = data.jumpStrength; // } private _computeForwardDirAndUpdateCamera() { if (!this.cameraAndControls) { forwardDir.copy(DEFAULT_FORWARD_DIR); return; } const {camera, controlsNode} = this.cameraAndControls; forwardDir.set(0, 0, -1).unproject(camera).sub(camera.position); forwardDir.y = 0; forwardDir.normalize(); if (controlsNode.target && controlsNode.setTarget) { controlsNode.target(currentTarget); delta.copy(this.object.position).sub(currentTarget); newCameraPosition.copy(camera.position).add(delta); camera.position.lerp(newCameraPosition, LERP); newTarget.copy(currentTarget).lerp(this.object.position, LERP); controlsNode.setTarget(newTarget); } } /** * * torque * */ initWithTorque(worldObject: Object3D, scene: PolyScene) { const cameraPath = CorePhysicsAttribute.getCharacterControllerCameraPath(this.object); if (cameraPath == null) { return; } const camera = CorePath.findObjectByMaskInObject(`*/${cameraPath}`, worldObject) as Camera; if (!camera) { return; } const controlsNode = CoreCameraControlsController.controlsNode({camera, scene}); if (!controlsNode) { return; } this.cameraAndControls = {camera, controlsNode}; } onGroundWithTorque(): boolean { return Math.abs(this.body.linvel().y) < 0.1; } // setInputDataWithTorque(inputData: CorePlayerPhysicsInputData) { // this._computeForwardDirAndUpdateCamera(); // // this._inputData.left = inputData.left; // // this._inputData.right = inputData.right; // // this._inputData.backward = inputData.backward; // // this._inputData.forward = inputData.forward; // } updateWithTorque( computeInputData: CorePlayerPhysicsComputeInputData, inputData: CorePlayerPhysicsInputData, delta: number ) { this._computeForwardDirAndUpdateCamera(); // reset forces this._userTorques.set(0, 0, 0); this._userImpulses.set(0, 0, 0); const running = computeInputData.runAllowed && inputData.run; // jump if (computeInputData.jumpAllowed && inputData.jump && this.onGround()) { this._userImpulses.y += computeInputData.jumpStrength * 100; } // torques const speed = running ? computeInputData.speed * computeInputData.runSpeedMult : computeInputData.speed; if (inputData.left && !inputData.right) { torqueLR.copy(forwardDir).multiplyScalar(-speed); this._userTorques.add(torqueLR); } else { if (inputData.right && !inputData.left) { torqueLR.copy(forwardDir).multiplyScalar(speed); this._userTorques.add(torqueLR); } } if (inputData.forward && !inputData.backward) { torqueFB.copy(forwardDir).cross(up).normalize().multiplyScalar(-speed); this._userTorques.add(torqueFB); } else { if (inputData.backward && !inputData.forward) { torqueFB.copy(forwardDir).cross(up).normalize().multiplyScalar(speed); this._userTorques.add(torqueFB); } } // reset if below threshold if (computeInputData.resetIfBelowThreshold) { if (this.body.translation().y < computeInputData.resetThreshold) { _physicsRBDResetAll(this.object, true); this.body.setTranslation(this.startPosition, true); } } // // update RBD // this.body.applyTorqueImpulse(this._userTorques, true); this.body.applyImpulse(this._userImpulses, true); } /** * * character controller * */ onGroundWithCharacterController() { return Math.abs(this._correctedMovement.y) < 0.01; } setInputDataWithCharacterController(inputData: CorePlayerPhysicsInputData) { // this._inputData.left = inputData.left; // this._inputData.right = inputData.right; // this._inputData.backward = inputData.backward; // this._inputData.forward = inputData.forward; // this._userForces.set(0, 0, 0); // const damping = 0.95; // if (this._computeInputData.runAllowed && inputData.run) { // this._inputData.run = true; // } else { // this._inputData.run = false; // } // // console.log(this._computeInputData.jumpAllowed, inputData.jump, this.onGround(), this._correctedMovement.y); // if (this._computeInputData.jumpAllowed && inputData.jump && this.onGround()) { // this._userForces.y += this._computeInputData.jumpStrength; // this._velocity.y = 0; // } // const speed = this._inputData.run // ? this._computeInputData.speed * this._computeInputData.runSpeedMult // : this._computeInputData.speed; // if (this._inputData.left) { // this._userForces.x = -speed; // if (this._velocity.x > 0) { // this._velocity.x = 0; // } // } else { // if (this._inputData.right) { // this._userForces.x = speed; // if (this._velocity.x < 0) { // this._velocity.x = 0; // } // } else { // this._userForces.x = 0; // this._velocity.x *= damping; // } // } // if (this._inputData.forward) { // this._userForces.z = -speed; // if (this._velocity.z > 0) { // this._velocity.z = 0; // } // } else { // if (this._inputData.backward) { // this._userForces.z = speed; // if (this._velocity.z < 0) { // this._velocity.z = 0; // } // } else { // this._userForces.z = 0; // this._velocity.z *= damping; // } // } } updateWithCharacterController( computeData: CorePlayerPhysicsComputeInputData, inputData: CorePlayerPhysicsInputData, delta: number ) { // const characterController = this.characterController!; // gravity.set(this.world.gravity.x, this.world.gravity.y, this.world.gravity.z); // forcesByDelta.copy(this._userForces).add(gravity).multiplyScalar(delta).divideScalar(this._mass); // this._velocity.add(forcesByDelta); // desiredTranslation.copy(this._velocity).multiplyScalar(delta); // // console.log(desiredTranslation.x); // // console.log(this.PhysicsLib.QueryFilterFlags, this.PhysicsLib.QueryFilterFlags.EXCLUDE_KINEMATIC); // characterController.computeColliderMovement( // this.collider, // desiredTranslation // // undefined, // // undefined, // // (collider) => { // // console.log(collider); // // return false; // // } // ); // const correctedMovement = characterController.computedMovement(); // this._correctedMovement.set(correctedMovement.x, correctedMovement.y, correctedMovement.z); // // console.log(correctedMovement.x, correctedMovement.y, correctedMovement.z); // // const y = correctedMovement.y > 0 && correctedMovement.y < 0.1 ? -0.1 : correctedMovement.y; // const deltaInv = 1 / delta; // // const fix = 12; // // console.log( // // 'x', // // this._velocity.x.toFixed(fix), // // desiredTranslation.x.toFixed(fix), // // correctedMovement.x.toFixed(fix), // // (correctedMovement.x * deltaInv).toFixed(fix), // // 'y', // // this._velocity.y.toFixed(fix), // // desiredTranslation.y.toFixed(fix), // // correctedMovement.y.toFixed(fix), // // (correctedMovement.y * deltaInv).toFixed(fix), // // Math.abs(this._velocity.x) < 0.000001 ? this.characterController.numComputedCollisions() : null // // ); // this._velocity.x = correctedMovement.x * deltaInv; // this._velocity.y = correctedMovement.y * deltaInv; // this._velocity.z = correctedMovement.z * deltaInv; // // temporary fix for https://github.com/dimforge/rapier.js/issues/191 // // const expectedVely = correctedMovement.y * deltaInv; // // this._velocity.y = Math.abs(expectedVely) < 0.1 ? this._velocity.y : expectedVely; // // if (expectedVely > 0 && this.characterController.numComputedCollisions() > 0) { // // // // but that fix breaks when doing auto step // // // console.log(this.characterController.numComputedCollisions()); // // this._velocity.y = -0.1; // // } // // this._velocity.set(correctedMovement.x, correctedMovement.y, correctedMovement.z).multiplyScalar(deltaInv); // // this._velocity.y = correctedTranslation.y; // // if (correctedMovement) { // // tmpPosition.set(result.correctedMovement.x, result.correctedMovement.y, result.correctedMovement.z); // // // tmpPosition.add(Object3D.position); // // console.log(tmpPosition.toArray()); // correctedTranslation.copy(this._correctedMovement).add(this.object.position); //.divideScalar(delta); // this.body.setNextKinematicTranslation(correctedTranslation); // // Object3D.position.add(tmpOffset); // // } // // TODO: use collisions // // const collisionsCount = this.characterController.numComputedCollisions() // // for (let i = 0; i < collisionsCount; i++) { // // // const collision = this.characterController.computedCollision(i); // // // Do something with that collision information. // // } } }