freeblox/packages/engine/src/gameobjects/physics.object.ts

106 lines
3.1 KiB
TypeScript

import { PhysicsTicking } from '../physics/ticking';
import type Rapier from '@dimforge/rapier3d';
import { PhysicalObject } from './physical.object';
import { Quaternion, Vector3 } from 'three';
import { EditorProperty } from '../decorators/property';
import { GameObject } from '../types/game-object';
export class PhysicsObject extends PhysicalObject implements PhysicsTicking {
public objectType = 'PhysicsObject';
isTickingObject = true;
protected collider?: Rapier.Collider;
public rigidBody?: Rapier.RigidBody;
protected physicsWorldRef?: Rapier.World;
@EditorProperty({ type: Boolean })
public canCollide = true;
@EditorProperty({ type: Boolean })
public anchored = true;
@EditorProperty({ type: Number })
public mass = 1;
@EditorProperty({ type: Number })
public friction = 0.2;
private _tempVec = new Vector3();
private _tempQuat = new Quaternion();
private _tempQuat2 = new Quaternion();
initialize(physicsEngine: typeof Rapier, physicsWorld: Rapier.World): void {
if (this.virtual) return;
this.physicsWorldRef = physicsWorld;
// This object has effecively no physics:
// doesn't move, doesn't collide
if (this.anchored && !this.canCollide) return;
let bodyDesc: Rapier.RigidBodyDesc;
if (this.anchored) bodyDesc = physicsEngine.RigidBodyDesc.fixed();
else bodyDesc = physicsEngine.RigidBodyDesc.dynamic();
const position = this.position.clone();
const rotation = this.quaternion.clone();
this.getWorldPosition(position);
this.getWorldQuaternion(rotation);
bodyDesc
.setTranslation(...position.toArray())
.setRotation(rotation)
.setAdditionalMass(this.mass)
.setLinearDamping(this.friction)
.setAngularDamping(this.friction);
const body = physicsWorld.createRigidBody(bodyDesc);
let collider: Rapier.Collider | undefined;
if (this.canCollide) {
collider = this.createCollider(physicsEngine, physicsWorld, body);
}
this.collider = collider;
this.rigidBody = body;
}
tick(dt: number): void {
if (!this.rigidBody || this.virtual) return;
this._tempVec.copy(this.rigidBody.translation() as any);
this._tempQuat.copy(this.rigidBody.rotation() as any);
if ((this.parent as GameObject)?.objectType !== 'World') {
this.parent?.worldToLocal(this._tempVec);
this.parent?.getWorldQuaternion(this._tempQuat2);
this._tempQuat2.invert();
this._tempQuat.premultiply(this._tempQuat2);
}
this.position.copy(this._tempVec);
this.quaternion.copy(this._tempQuat);
}
dispose(): void {
if (this.collider && !this.rigidBody) {
this.physicsWorldRef?.removeCollider(this.collider, false);
}
if (this.rigidBody) {
this.physicsWorldRef?.removeRigidBody(this.rigidBody);
}
}
/**
* Create collision shape for physics object.
* @internal
* @param factory Physics engine
* @param world Physics world
* @param body RigidBody
*/
protected createCollider(
factory: typeof Rapier,
world: Rapier.World,
body?: Rapier.RigidBody
): Rapier.Collider | undefined {
return undefined;
}
}