@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
171 lines (170 loc) • 6.43 kB
JavaScript
"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;
}
}