threefiz
Version:
Implementation of a physics engine based on the [Three.js](https://github.com/mrdoob/three.js) library.
129 lines (112 loc) • 3.53 kB
text/typescript
import Cuboid from "./Cuboid";
import { Scene, Vector3 } from "three";
import World from "./World";
import RigidBody, { RigidBodyProps } from "./RigidBody";
import Sphere from "./Sphere";
import AABBTree from "./AABBTree/AABBTree";
const v = new Vector3();
const tree = new AABBTree();
let RESULTS: Uint16Array<ArrayBuffer>;
type Props = {
scene: Scene;
timeStep: number;
gravity: Vector3;
};
class ThreeFiz {
private dT: number = 0.0;
private accumulator: number = 0.0;
private lastTicks: number = 0.0;
objects: RigidBody[] = [];
private readonly scene: Scene;
private readonly world: World;
private isPaused: boolean = false;
constructor({
scene = new Scene(),
gravity = new Vector3(0, -9.8, 0),
}: Partial<Props> = {}) {
this.scene = scene;
this.world = new World(gravity);
document.addEventListener("visibilitychange", this.visibilityChange);
}
addBox(object: Partial<RigidBodyProps>): void {
const newObject = new Cuboid(object);
this.objects.push(newObject);
this.scene.add(newObject.mesh);
newObject.mesh.updateMatrix();
newObject.mesh.updateMatrixWorld();
newObject.updateAABB();
newObject.updateCollider();
tree.insert(
newObject.aabb,
this.objects.length - 1,
`${newObject.constructor.name}${this.objects.length - 1}`
);
newObject.mesh.position.copy(newObject.getPosition(v));
}
addSphere(object: Partial<RigidBodyProps>): void {
const newObject = new Sphere(object);
this.objects.push(newObject);
this.scene.add(newObject.mesh);
newObject.updateAABB();
newObject.updateCollider();
tree.insert(
newObject.aabb,
this.objects.length - 1,
`${newObject.constructor.name}${this.objects.length - 1}`
);
newObject.mesh.position.copy(newObject.getPosition(v));
}
init(): void {
this.lastTicks = Date.now();
this.world.updateObjects(this.objects, tree, 0);
const N = this.objects.length;
RESULTS = new Uint16Array(N);
}
setGravity(gravity: Vector3): void {
this.world.setGravity(gravity);
}
private update(dT: number): void {
this.world.updateObjects(this.objects, tree, dT);
this.detectCollisions();
}
pause(): void {
this.isPaused = true;
}
resume(): void {
this.isPaused = false;
}
onCollision(_object1: RigidBody, _object2: RigidBody): void {}
private detectCollisions(): void {
let objB: RigidBody;
let maxResults: number;
this.objects.forEach((objA, idx) => {
maxResults = tree.query(RESULTS, objA.aabb, idx);
for (let i = 0; i < maxResults; i++) {
if (idx < RESULTS[i]) {
objB = this.objects[RESULTS[i]];
if (!objA.isStatic || !objB.isStatic) {
if (objA.intersects(objB)) {
objA.resolveCollision(objB);
this.onCollision(objA, objB);
}
}
}
}
});
}
visibilityChange = () => {
if (!document.hidden) this.lastTicks = Date.now();
};
step(): void {
const fixedTimeStep = 0.001;
this.accumulator += this.dT;
while (this.accumulator >= fixedTimeStep) {
if (!this.isPaused) this.update(fixedTimeStep);
this.accumulator -= fixedTimeStep;
}
const currentTime = Date.now();
this.dT = (currentTime - this.lastTicks) / 1000;
this.lastTicks = currentTime;
}
}
export default ThreeFiz;