UNPKG

@polygonjs/polygonjs

Version:

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

171 lines (170 loc) 6.43 kB
"use strict"; import { CapsuleSopOperation } from "./../../engine/operations/sop/Capsule"; import { Vector3, Box3, Line3, Matrix4 } from "three"; function capsuleDataFromInput(input) { return { radius: input.radius, segment: new Line3(new Vector3(0, 0, 0), new Vector3(0, -(input.height - 2 * input.radius), 0)) }; } const tmpGravity = new Vector3(0, 0, 0); const upVector = new Vector3(0, 1, 0); const tempVector1 = new Vector3(); const tempVector2 = new Vector3(); const tempVector3 = new Vector3(); const tempVector4 = new Vector3(); const tempVector5 = new Vector3(); const tmpPos1 = new Vector3(); const tempBox = new Box3(); const tempMat = new Matrix4(); const tempSegment = new Line3(); export class CorePlayer { constructor(_object) { this._object = _object; this._inputData = { forward: false, backward: false, left: false, right: false, run: false, jump: false }; this._computeInputData = { collider: void 0, speed: 1, runAllowed: true, runSpeedMult: 2, jumpAllowed: true, jumpStrength: 1, physicsSteps: 5, gravity: new Vector3(0, -9.8, 0), capsuleData: capsuleDataFromInput({ radius: CapsuleSopOperation.DEFAULT_PARAMS.radius, height: CapsuleSopOperation.DEFAULT_PARAMS.height }) }; this._computedData = { velocityFromForces: new Vector3(0, 0, 0), onGround: false, velocityFromPositionDelta: new Vector3(0, 0, 0) }; } setComputeInputData(data) { this._computeInputData.collider = data.collider; this._computeInputData.speed = data.speed; this._computeInputData.runSpeedMult = data.runSpeedMult; this._computeInputData.jumpStrength = data.jumpStrength; this._computeInputData.physicsSteps = data.physicsSteps; this._computeInputData.gravity.copy(data.gravity); this._computeInputData.speed = data.speed; this._computeInputData.capsuleData = capsuleDataFromInput(data.capsuleInput); } update(delta) { const deltaBounded = Math.min(delta, 0.1); const physicsSteps = this._computeInputData.physicsSteps; const deltaNormalized = deltaBounded / physicsSteps; tmpPos1.copy(this._object.position); for (let i = 0; i < physicsSteps; i++) { this._updateStep(deltaNormalized); } this._computedData.velocityFromPositionDelta.copy(this._object.position).sub(tmpPos1).divideScalar(delta); } _updateStep(delta) { const object = this._object; const { onGround, velocityFromForces } = this._computedData; const { collider, speed, runSpeedMult, gravity, capsuleData } = this._computeInputData; const { left, right, backward, forward, run } = this._inputData; if (!onGround) { tmpGravity.copy(gravity).multiplyScalar(delta); velocityFromForces.add(tmpGravity); } object.position.addScaledVector(velocityFromForces, delta); const angle = 0; const speedNormalized = speed * delta * (run ? runSpeedMult : 1); tempVector2.set(0, 0, 0); if (forward) { tempVector1.set(0, 0, -1).applyAxisAngle(upVector, angle); tempVector2.add(tempVector1); } if (backward) { tempVector1.set(0, 0, 1).applyAxisAngle(upVector, angle); tempVector2.add(tempVector1); } if (left) { tempVector1.set(-1, 0, 0).applyAxisAngle(upVector, angle); tempVector2.add(tempVector1); } if (right) { tempVector1.set(1, 0, 0).applyAxisAngle(upVector, angle); tempVector2.add(tempVector1); } tempVector2.normalize().multiplyScalar(speedNormalized); object.position.add(tempVector2); object.updateMatrix(); object.updateMatrixWorld(); if (collider) { tempBox.makeEmpty(); tempMat.copy(collider.matrixWorld).invert(); tempSegment.copy(capsuleData.segment); tempSegment.start.applyMatrix4(object.matrixWorld).applyMatrix4(tempMat); tempSegment.end.applyMatrix4(object.matrixWorld).applyMatrix4(tempMat); tempBox.expandByPoint(tempSegment.start); tempBox.expandByPoint(tempSegment.end); tempBox.min.addScalar(-capsuleData.radius); tempBox.max.addScalar(capsuleData.radius); const intersectsBounds = (box, isLeaf, score, depth, nodeIndex) => { return box.intersectsBox(tempBox); }; const intersectsTriangle = (tri) => { const triPoint = tempVector3; const capsulePoint = tempVector4; const distance = tri.closestPointToSegment(tempSegment, triPoint, capsulePoint); if (distance < capsuleData.radius) { const depth = capsuleData.radius - distance; const direction = capsulePoint.sub(triPoint).normalize(); tempSegment.start.addScaledVector(direction, depth); tempSegment.end.addScaledVector(direction, depth); } }; collider.geometry.boundsTree.shapecast({ intersectsBounds, intersectsTriangle }); const newPosition = tempVector5; newPosition.copy(tempSegment.start); newPosition.applyMatrix4(collider.matrixWorld); const deltaVector = tempVector2; deltaVector.subVectors(newPosition, object.position); this._computedData.onGround = deltaVector.y > Math.abs(delta * velocityFromForces.y * 0.25); const offset = Math.max(0, deltaVector.length() - 1e-5); deltaVector.normalize().multiplyScalar(offset); object.position.add(deltaVector); if (!this._computedData.onGround) { deltaVector.normalize(); velocityFromForces.addScaledVector(deltaVector, -deltaVector.dot(velocityFromForces)); } else { velocityFromForces.set(0, 0, 0); } } } setInputData(inputData) { this._inputData.left = inputData.left; this._inputData.right = inputData.right; this._inputData.backward = inputData.backward; this._inputData.forward = inputData.forward; if (this._computeInputData.runAllowed && inputData.run && this._computedData.onGround) { this._inputData.run = true; } else { this._inputData.run = false; } if (this._computeInputData.jumpAllowed && inputData.jump && this._computedData.onGround) { this._computedData.velocityFromForces.y = this._computeInputData.jumpStrength; } } velocityFromPositionDelta(target) { return target.copy(this._computedData.velocityFromPositionDelta); } onGround() { return this._computedData.onGround; } }