playcanvas
Version:
Open-source WebGL/WebGPU 3D engine for the web
418 lines (417 loc) • 9.83 kB
JavaScript
import { Quat } from "../../../core/math/quat.js";
import { Vec3 } from "../../../core/math/vec3.js";
import { Component } from "../component.js";
import {
BODYTYPE_STATIC,
BODYGROUP_DYNAMIC,
BODYGROUP_KINEMATIC,
BODYGROUP_STATIC,
BODYMASK_ALL,
BODYMASK_NOT_STATIC,
BODYTYPE_DYNAMIC,
BODYTYPE_KINEMATIC
} from "./constants.js";
const _quat1 = new Quat();
const _quat2 = new Quat();
const _vec3 = new Vec3();
const _position = new Vec3();
const _rotation = new Quat();
const _vecA = new Vec3();
const _vecB = new Vec3();
class RigidBodyComponent extends Component {
static EVENT_CONTACT = "contact";
static EVENT_COLLISIONSTART = "collisionstart";
static EVENT_COLLISIONEND = "collisionend";
static EVENT_TRIGGERENTER = "triggerenter";
static EVENT_TRIGGERLEAVE = "triggerleave";
static order = -1;
_angularDamping = 0;
_angularFactor = new Vec3(1, 1, 1);
_angularVelocity = new Vec3();
_body = null;
_friction = 0.5;
_group = BODYGROUP_STATIC;
_linearDamping = 0;
_linearFactor = new Vec3(1, 1, 1);
_linearVelocity = new Vec3();
_mask = BODYMASK_NOT_STATIC;
_mass = 1;
_restitution = 0;
_rollingFriction = 0;
_simulationEnabled = false;
_type = BODYTYPE_STATIC;
set angularDamping(damping) {
if (this._angularDamping !== damping) {
this._angularDamping = damping;
if (this._body) {
this._body.setDamping(this._linearDamping, damping);
}
}
}
get angularDamping() {
return this._angularDamping;
}
set angularFactor(factor) {
if (!this._angularFactor.equals(factor)) {
this._angularFactor.copy(factor);
if (this._body && this._type === BODYTYPE_DYNAMIC) {
this._body.setAngularFactor(factor);
}
}
}
get angularFactor() {
return this._angularFactor;
}
set angularVelocity(velocity) {
if (this._body && this._type === BODYTYPE_DYNAMIC) {
this._body.activate();
this._body.setAngularVelocity(velocity);
this._angularVelocity.copy(velocity);
}
}
get angularVelocity() {
if (this._body && this._type === BODYTYPE_DYNAMIC) {
this._body.getAngularVelocity(this._angularVelocity);
}
return this._angularVelocity;
}
set body(body) {
if (this._body !== body) {
this._body = body;
if (body && this._simulationEnabled) {
body.activate();
}
}
}
get body() {
return this._body ? this._body.nativeBody : null;
}
set friction(friction) {
if (this._friction !== friction) {
this._friction = friction;
if (this._body) {
this._body.setFriction(friction);
}
}
}
get friction() {
return this._friction;
}
set group(group) {
if (this._group !== group) {
this._group = group;
if (this.enabled && this.entity.enabled) {
this.disableSimulation();
this.enableSimulation();
}
}
}
get group() {
return this._group;
}
set linearDamping(damping) {
if (this._linearDamping !== damping) {
this._linearDamping = damping;
if (this._body) {
this._body.setDamping(damping, this._angularDamping);
}
}
}
get linearDamping() {
return this._linearDamping;
}
set linearFactor(factor) {
if (!this._linearFactor.equals(factor)) {
this._linearFactor.copy(factor);
if (this._body && this._type === BODYTYPE_DYNAMIC) {
this._body.setLinearFactor(factor);
}
}
}
get linearFactor() {
return this._linearFactor;
}
set linearVelocity(velocity) {
if (this._body && this._type === BODYTYPE_DYNAMIC) {
this._body.activate();
this._body.setLinearVelocity(velocity);
this._linearVelocity.copy(velocity);
}
}
get linearVelocity() {
if (this._body && this._type === BODYTYPE_DYNAMIC) {
this._body.getLinearVelocity(this._linearVelocity);
}
return this._linearVelocity;
}
set mask(mask) {
if (this._mask !== mask) {
this._mask = mask;
if (this.enabled && this.entity.enabled) {
this.disableSimulation();
this.enableSimulation();
}
}
}
get mask() {
return this._mask;
}
set mass(mass) {
if (this._mass !== mass) {
this._mass = mass;
if (this._body && this._type === BODYTYPE_DYNAMIC) {
const enabled = this.enabled && this.entity.enabled;
if (enabled) {
this.disableSimulation();
}
this._body.setMass(mass);
if (enabled) {
this.enableSimulation();
}
}
}
}
get mass() {
return this._mass;
}
set restitution(restitution) {
if (this._restitution !== restitution) {
this._restitution = restitution;
if (this._body) {
this._body.setRestitution(restitution);
}
}
}
get restitution() {
return this._restitution;
}
set rollingFriction(friction) {
if (this._rollingFriction !== friction) {
this._rollingFriction = friction;
if (this._body) {
this._body.setRollingFriction(friction);
}
}
}
get rollingFriction() {
return this._rollingFriction;
}
set type(type) {
if (this._type !== type) {
this._type = type;
this.disableSimulation();
switch (type) {
case BODYTYPE_DYNAMIC:
this._group = BODYGROUP_DYNAMIC;
this._mask = BODYMASK_ALL;
break;
case BODYTYPE_KINEMATIC:
this._group = BODYGROUP_KINEMATIC;
this._mask = BODYMASK_ALL;
break;
case BODYTYPE_STATIC:
default:
this._group = BODYGROUP_STATIC;
this._mask = BODYMASK_NOT_STATIC;
break;
}
this.createBody();
}
}
get type() {
return this._type;
}
createBody() {
const entity = this.entity;
let shape;
if (entity.collision) {
shape = entity.collision.shape;
if (entity.trigger) {
entity.trigger.destroy();
delete entity.trigger;
}
}
const world = this.system.physicsWorld;
if (shape && world) {
if (this._body) {
world.removeBody(this._body);
world.destroyBody(this._body);
this._body = null;
}
const mass = this._type === BODYTYPE_DYNAMIC ? this._mass : 0;
this._getEntityTransform(_position, _rotation);
const body = world.createBody({
type: this._type,
mass,
shape,
position: _position,
rotation: _rotation,
entity
});
body.setRestitution(this._restitution);
body.setFriction(this._friction);
body.setRollingFriction(this._rollingFriction);
body.setDamping(this._linearDamping, this._angularDamping);
if (this._type === BODYTYPE_DYNAMIC) {
body.setLinearFactor(this._linearFactor);
body.setAngularFactor(this._angularFactor);
}
this.body = body;
if (this.enabled && entity.enabled) {
this.enableSimulation();
}
}
}
isActive() {
return this._body ? this._body.isActive() : false;
}
activate() {
if (this._body) {
this._body.activate();
}
}
enableSimulation() {
this.system.enableSimulation(this);
}
disableSimulation() {
this.system.disableSimulation(this);
}
applyForce(x, y, z, px, py, pz) {
const body = this._body;
if (body) {
body.activate();
if (x instanceof Vec3) {
_vecA.copy(x);
} else {
_vecA.set(x, y, z);
}
if (y instanceof Vec3) {
_vecB.copy(y);
} else if (px !== void 0) {
_vecB.set(px, py, pz);
} else {
_vecB.set(0, 0, 0);
}
body.applyForce(_vecA, _vecB);
}
}
applyTorque(x, y, z) {
const body = this._body;
if (body) {
body.activate();
if (x instanceof Vec3) {
_vecA.copy(x);
} else {
_vecA.set(x, y, z);
}
body.applyTorque(_vecA);
}
}
applyImpulse(x, y, z, px, py, pz) {
const body = this._body;
if (body) {
body.activate();
if (x instanceof Vec3) {
_vecA.copy(x);
} else {
_vecA.set(x, y, z);
}
if (y instanceof Vec3) {
_vecB.copy(y);
} else if (px !== void 0) {
_vecB.set(px, py, pz);
} else {
_vecB.set(0, 0, 0);
}
body.applyImpulse(_vecA, _vecB);
}
}
applyTorqueImpulse(x, y, z) {
const body = this._body;
if (body) {
body.activate();
if (x instanceof Vec3) {
_vecA.copy(x);
} else {
_vecA.set(x, y, z);
}
body.applyTorqueImpulse(_vecA);
}
}
isStatic() {
return this._type === BODYTYPE_STATIC;
}
isStaticOrKinematic() {
return this._type === BODYTYPE_STATIC || this._type === BODYTYPE_KINEMATIC;
}
isKinematic() {
return this._type === BODYTYPE_KINEMATIC;
}
_getEntityTransform(position, rotation) {
const entity = this.entity;
const component = entity.collision;
if (component) {
position.copy(component.getShapePosition());
rotation.copy(component.getShapeRotation());
} else {
position.copy(entity.getPosition());
rotation.copy(entity.getRotation());
}
}
syncEntityToBody() {
const body = this._body;
if (body) {
this._getEntityTransform(_position, _rotation);
body.setTransform(_position, _rotation);
}
}
_updateDynamic() {
const body = this._body;
if (body.isActive()) {
const entity = this.entity;
body.getTransform(_position, _rotation);
const component = entity.collision;
if (component && component._hasOffset) {
const lo = component.linearOffset;
const ao = component.angularOffset;
const invertedAo = _quat2.copy(ao).invert();
const entityRot = _quat1.copy(_rotation).mul(invertedAo);
entityRot.transformVector(lo, _vec3);
entity.setPositionAndRotation(_position.sub(_vec3), entityRot);
} else {
entity.setPositionAndRotation(_position, _rotation);
}
}
}
_updateKinematic() {
this._getEntityTransform(_position, _rotation);
this._body.setKinematicTarget(_position, _rotation);
}
teleport(x, y, z, rx, ry, rz) {
if (x instanceof Vec3) {
this.entity.setPosition(x);
} else {
this.entity.setPosition(x, y, z);
}
if (y instanceof Quat) {
this.entity.setRotation(y);
} else if (y instanceof Vec3) {
this.entity.setEulerAngles(y);
} else if (rx !== void 0) {
this.entity.setEulerAngles(rx, ry, rz);
}
this.syncEntityToBody();
}
onEnable() {
if (!this._body) {
this.createBody();
}
this.enableSimulation();
}
onDisable() {
this.disableSimulation();
}
}
export {
RigidBodyComponent
};