UNPKG

playcanvas

Version:

Open-source WebGL/WebGPU 3D engine for the web

346 lines (345 loc) 8.97 kB
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"; _type = "box"; _halfExtents = new Vec3(0.5, 0.5, 0.5); _linearOffset = new Vec3(); _angularOffset = new Quat(); _radius = 0.5; _axis = 1; _height = 2; _asset = null; _renderAsset = null; _convexHull = false; _shape = null; _model = null; _render = null; _checkVertexDuplicates = true; _initialized = false; _compoundParent = null; _hasOffset = false; _builtWorldScale = null; constructor(system, entity) { super(system, entity); this.entity.on("insert", this._onInsert, this); } set type(arg) { if (this._type === arg) { return; } const previous = this._type; this._type = arg; this.system.changeType(this, previous, arg); } get type() { return this._type; } set halfExtents(arg) { if (arg instanceof Vec3) { this._halfExtents.copy(arg); } else { this._halfExtents.set(arg[0], arg[1], arg[2]); } if (this._initialized && this._type === "box") { this.system.recreatePhysicalShapes(this); } } get halfExtents() { return this._halfExtents; } set linearOffset(arg) { if (arg instanceof Vec3) { this._linearOffset.copy(arg); } else { this._linearOffset.set(arg[0], arg[1], arg[2]); } this._updateHasOffset(); if (this._initialized) { this.system.recreatePhysicalShapes(this); } } get linearOffset() { return this._linearOffset; } set angularOffset(arg) { if (arg instanceof Quat) { this._angularOffset.copy(arg); } else if (arg.length === 3) { this._angularOffset.setFromEulerAngles(arg[0], arg[1], arg[2]); } else { this._angularOffset.set(arg[0], arg[1], arg[2], arg[3]); } this._updateHasOffset(); if (this._initialized) { this.system.recreatePhysicalShapes(this); } } get angularOffset() { return this._angularOffset; } set radius(arg) { this._radius = arg; const t = this._type; if (this._initialized && (t === "sphere" || t === "capsule" || t === "cylinder" || t === "cone")) { this.system.recreatePhysicalShapes(this); } } get radius() { return this._radius; } set axis(arg) { this._axis = arg; const t = this._type; if (this._initialized && (t === "capsule" || t === "cylinder" || t === "cone")) { this.system.recreatePhysicalShapes(this); } } get axis() { return this._axis; } set height(arg) { this._height = arg; const t = this._type; if (this._initialized && (t === "capsule" || t === "cylinder" || t === "cone")) { this.system.recreatePhysicalShapes(this); } } get height() { return this._height; } set asset(arg) { const assets = this.system.app.assets; if (this._asset) { const asset = assets.get(this._asset); if (asset) { asset.off("remove", this.onAssetRemoved, this); } } this._asset = arg instanceof Asset ? arg.id : arg; if (arg) { const asset = assets.get(this._asset); if (asset) { asset.off("remove", this.onAssetRemoved, this); asset.on("remove", this.onAssetRemoved, this); } } if (this._initialized && this._type === "mesh") { if (!arg) { this._model = null; } this.system.recreatePhysicalShapes(this); } } get asset() { return this._asset; } set renderAsset(arg) { const assets = this.system.app.assets; if (this._renderAsset) { const asset = assets.get(this._renderAsset); if (asset) { asset.off("remove", this.onRenderAssetRemoved, this); } } this._renderAsset = arg instanceof Asset ? arg.id : arg; if (arg) { const asset = assets.get(this._renderAsset); if (asset) { asset.off("remove", this.onRenderAssetRemoved, this); asset.on("remove", this.onRenderAssetRemoved, this); } } if (this._initialized && this._type === "mesh") { if (!arg) { this._render = null; } this.system.recreatePhysicalShapes(this); } } get renderAsset() { return this._renderAsset; } set convexHull(arg) { this._convexHull = arg; if (this._initialized && this._type === "mesh") { this.system.doRecreatePhysicalShape(this); } } get convexHull() { return this._convexHull; } set shape(arg) { this._shape = arg; } get shape() { return this._shape; } set model(arg) { this._model = arg; if (this._initialized && this._type === "mesh") { this.system.doRecreatePhysicalShape(this); } } get model() { return this._model; } set render(arg) { this._render = arg; if (this._initialized && this._type === "mesh") { this.system.doRecreatePhysicalShape(this); } } get render() { return this._render; } set checkVertexDuplicates(arg) { this._checkVertexDuplicates = arg; } get checkVertexDuplicates() { return this._checkVertexDuplicates; } _updateHasOffset() { this._hasOffset = !this._linearOffset.equals(Vec3.ZERO) || !this._angularOffset.equals(Quat.IDENTITY); } onAssetRemoved(asset) { asset.off("remove", this.onAssetRemoved, this); if (this._asset === asset.id) { this.asset = null; } } onRenderAssetRemoved(asset) { asset.off("remove", this.onRenderAssetRemoved, this); if (this._renderAsset === asset.id) { this.renderAsset = null; } } _onInsert(parent) { const world = this.system.physicsWorld; if (!world) { 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 (world.getCompoundChildCount(ancestor.collision.shape) === 0) { this.system.recreatePhysicalShapes(ancestor.collision); } else { this.system.recreatePhysicalShapes(this); } break; } ancestor = ancestor.parent; } } } _updateEachDescendantTransform(entity) { if (!entity.collision || entity.collision._compoundParent !== this.collision._compoundParent) { return; } this.collision.system.updateCompoundChildTransform(entity, false); } _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._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._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._angularOffset); } return rot; } onEnable() { if (this._type === "mesh" && (this._asset || this._renderAsset) && this._initialized) { const asset = this.system.app.assets.get(this._asset || this._renderAsset); if (asset && (!asset.resource || !this._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.system.physicsWorld.getCompoundChildCount(this._compoundParent.shape) === 0) { this.system.recreatePhysicalShapes(this._compoundParent); } else { this.system.updateCompoundChildTransform(this.entity, true); 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._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 };