playcanvas
Version:
Open-source WebGL/WebGPU 3D engine for the web
359 lines (358 loc) • 9.84 kB
JavaScript
import { Quat } from "../../../core/math/quat.js";
import { Vec3 } from "../../../core/math/vec3.js";
import { Asset } from "../../asset/asset.js";
import { Component } from "../component.js";
const _vec3 = new Vec3();
const _quat = new Quat();
class CollisionComponent extends Component {
static EVENT_CONTACT = "contact";
static EVENT_COLLISIONSTART = "collisionstart";
static EVENT_COLLISIONEND = "collisionend";
static EVENT_TRIGGERENTER = "triggerenter";
static EVENT_TRIGGERLEAVE = "triggerleave";
_compoundParent = null;
_hasOffset = false;
constructor(system, entity) {
super(system, entity);
this.entity.on("insert", this._onInsert, this);
this.on("set_type", this.onSetType, this);
this.on("set_convexHull", this.onSetModel, this);
this.on("set_halfExtents", this.onSetHalfExtents, this);
this.on("set_linearOffset", this.onSetOffset, this);
this.on("set_angularOffset", this.onSetOffset, this);
this.on("set_radius", this.onSetRadius, this);
this.on("set_height", this.onSetHeight, this);
this.on("set_axis", this.onSetAxis, this);
this.on("set_asset", this.onSetAsset, this);
this.on("set_renderAsset", this.onSetRenderAsset, this);
this.on("set_model", this.onSetModel, this);
this.on("set_render", this.onSetRender, this);
}
set enabled(arg) {
this._setValue("enabled", arg);
}
get enabled() {
return this.data.enabled;
}
set type(arg) {
this._setValue("type", arg);
}
get type() {
return this.data.type;
}
set halfExtents(arg) {
this._setValue("halfExtents", arg);
}
get halfExtents() {
return this.data.halfExtents;
}
set linearOffset(arg) {
this._setValue("linearOffset", arg);
}
get linearOffset() {
return this.data.linearOffset;
}
set angularOffset(arg) {
this._setValue("angularOffset", arg);
}
get angularOffset() {
return this.data.angularOffset;
}
set radius(arg) {
this._setValue("radius", arg);
}
get radius() {
return this.data.radius;
}
set axis(arg) {
this._setValue("axis", arg);
}
get axis() {
return this.data.axis;
}
set height(arg) {
this._setValue("height", arg);
}
get height() {
return this.data.height;
}
set asset(arg) {
this._setValue("asset", arg);
}
get asset() {
return this.data.asset;
}
set renderAsset(arg) {
this._setValue("renderAsset", arg);
}
get renderAsset() {
return this.data.renderAsset;
}
set convexHull(arg) {
this._setValue("convexHull", arg);
}
get convexHull() {
return this.data.convexHull;
}
set shape(arg) {
this._setValue("shape", arg);
}
get shape() {
return this.data.shape;
}
set model(arg) {
this._setValue("model", arg);
}
get model() {
return this.data.model;
}
set render(arg) {
this._setValue("render", arg);
}
get render() {
return this.data.render;
}
set checkVertexDuplicates(arg) {
this._setValue("checkVertexDuplicates", arg);
}
get checkVertexDuplicates() {
return this.data.checkVertexDuplicates;
}
_setValue(name, value) {
const data = this.data;
const oldValue = data[name];
data[name] = value;
this.fire("set", name, oldValue, value);
}
onSetType(name, oldValue, newValue) {
if (oldValue !== newValue) {
this.system.changeType(this, oldValue, newValue);
}
}
onSetHalfExtents(name, oldValue, newValue) {
const t = this.data.type;
if (this.data.initialized && t === "box") {
this.system.recreatePhysicalShapes(this);
}
}
onSetOffset(name, oldValue, newValue) {
this._hasOffset = !this.data.linearOffset.equals(Vec3.ZERO) || !this.data.angularOffset.equals(Quat.IDENTITY);
if (this.data.initialized) {
this.system.recreatePhysicalShapes(this);
}
}
onSetRadius(name, oldValue, newValue) {
const t = this.data.type;
if (this.data.initialized && (t === "sphere" || t === "capsule" || t === "cylinder" || t === "cone")) {
this.system.recreatePhysicalShapes(this);
}
}
onSetHeight(name, oldValue, newValue) {
const t = this.data.type;
if (this.data.initialized && (t === "capsule" || t === "cylinder" || t === "cone")) {
this.system.recreatePhysicalShapes(this);
}
}
onSetAxis(name, oldValue, newValue) {
const t = this.data.type;
if (this.data.initialized && (t === "capsule" || t === "cylinder" || t === "cone")) {
this.system.recreatePhysicalShapes(this);
}
}
onSetAsset(name, oldValue, newValue) {
const assets = this.system.app.assets;
if (oldValue) {
const asset = assets.get(oldValue);
if (asset) {
asset.off("remove", this.onAssetRemoved, this);
}
}
if (newValue) {
if (newValue instanceof Asset) {
this.data.asset = newValue.id;
}
const asset = assets.get(this.data.asset);
if (asset) {
asset.off("remove", this.onAssetRemoved, this);
asset.on("remove", this.onAssetRemoved, this);
}
}
if (this.data.initialized && this.data.type === "mesh") {
if (!newValue) {
this.data.model = null;
}
this.system.recreatePhysicalShapes(this);
}
}
onSetRenderAsset(name, oldValue, newValue) {
const assets = this.system.app.assets;
if (oldValue) {
const asset = assets.get(oldValue);
if (asset) {
asset.off("remove", this.onRenderAssetRemoved, this);
}
}
if (newValue) {
if (newValue instanceof Asset) {
this.data.renderAsset = newValue.id;
}
const asset = assets.get(this.data.renderAsset);
if (asset) {
asset.off("remove", this.onRenderAssetRemoved, this);
asset.on("remove", this.onRenderAssetRemoved, this);
}
}
if (this.data.initialized && this.data.type === "mesh") {
if (!newValue) {
this.data.render = null;
}
this.system.recreatePhysicalShapes(this);
}
}
onSetModel(name, oldValue, newValue) {
if (this.data.initialized && this.data.type === "mesh") {
this.system.implementations.mesh.doRecreatePhysicalShape(this);
}
}
onSetRender(name, oldValue, newValue) {
this.onSetModel(name, oldValue, newValue);
}
onAssetRemoved(asset) {
asset.off("remove", this.onAssetRemoved, this);
if (this.data.asset === asset.id) {
this.asset = null;
}
}
onRenderAssetRemoved(asset) {
asset.off("remove", this.onRenderAssetRemoved, this);
if (this.data.renderAsset === asset.id) {
this.renderAsset = null;
}
}
getCompoundChildShapeIndex(shape) {
const compound = this.data.shape;
const shapes = compound.getNumChildShapes();
for (let i = 0; i < shapes; i++) {
const childShape = compound.getChildShape(i);
if (Ammo.getPointer(childShape) === Ammo.getPointer(shape)) {
return i;
}
}
return null;
}
_onInsert(parent) {
if (typeof Ammo === "undefined") {
return;
}
if (this._compoundParent) {
this.system.recreatePhysicalShapes(this);
} else if (!this.entity.rigidbody) {
let ancestor = this.entity.parent;
while (ancestor) {
if (ancestor.collision && ancestor.collision.type === "compound") {
if (ancestor.collision.shape.getNumChildShapes() === 0) {
this.system.recreatePhysicalShapes(ancestor.collision);
} else {
this.system.recreatePhysicalShapes(this);
}
break;
}
ancestor = ancestor.parent;
}
}
}
_updateCompound() {
const entity = this.entity;
if (entity._dirtyWorld) {
let dirty = entity._dirtyLocal;
let parent = entity;
while (parent && !dirty) {
if (parent.collision && parent.collision === this._compoundParent) {
break;
}
if (parent._dirtyLocal) {
dirty = true;
}
parent = parent.parent;
}
if (dirty) {
entity.forEach(this.system.implementations.compound._updateEachDescendantTransform, entity);
const bodyComponent = this._compoundParent.entity.rigidbody;
if (bodyComponent) {
bodyComponent.activate();
}
}
}
}
getShapePosition() {
const pos = this.entity.getPosition();
if (this._hasOffset) {
const rot = this.entity.getRotation();
const lo = this.data.linearOffset;
_quat.copy(rot).transformVector(lo, _vec3);
return _vec3.add(pos);
}
return pos;
}
getShapeRotation() {
const rot = this.entity.getRotation();
if (this._hasOffset) {
return _quat.copy(rot).mul(this.data.angularOffset);
}
return rot;
}
onEnable() {
if (this.data.type === "mesh" && (this.data.asset || this.data.renderAsset) && this.data.initialized) {
const asset = this.system.app.assets.get(this.data.asset || this.data.renderAsset);
if (asset && (!asset.resource || !this.data.shape)) {
this.system.recreatePhysicalShapes(this);
return;
}
}
if (this.entity.rigidbody) {
if (this.entity.rigidbody.enabled) {
this.entity.rigidbody.enableSimulation();
}
} else if (this._compoundParent && this !== this._compoundParent) {
if (this._compoundParent.shape.getNumChildShapes() === 0) {
this.system.recreatePhysicalShapes(this._compoundParent);
} else {
const transform = this.system._getNodeTransform(this.entity, this._compoundParent.entity);
this._compoundParent.shape.addChildShape(transform, this.data.shape);
Ammo.destroy(transform);
if (this._compoundParent.entity.rigidbody) {
this._compoundParent.entity.rigidbody.activate();
}
}
} else if (this.entity.trigger) {
this.entity.trigger.enable();
}
}
onDisable() {
if (this.entity.rigidbody) {
this.entity.rigidbody.disableSimulation();
} else if (this._compoundParent && this !== this._compoundParent) {
if (!this._compoundParent.entity._destroying) {
this.system._removeCompoundChild(this._compoundParent, this.data.shape);
if (this._compoundParent.entity.rigidbody) {
this._compoundParent.entity.rigidbody.activate();
}
}
} else if (this.entity.trigger) {
this.entity.trigger.disable();
}
}
onBeforeRemove() {
if (this.asset) {
this.asset = null;
}
if (this.renderAsset) {
this.renderAsset = null;
}
this.entity.off("insert", this._onInsert, this);
this.off();
}
}
export {
CollisionComponent
};