UNPKG

@animech-public/playcanvas

Version:
2 lines (1 loc) 12.4 kB
import{Mat4 as e}from"../../../core/math/mat4.js";import{Quat as t}from"../../../core/math/quat.js";import{Vec3 as a}from"../../../core/math/vec3.js";import{SEMANTIC_POSITION as s}from"../../../platform/graphics/constants.js";import{GraphNode as n}from"../../../scene/graph-node.js";import{Model as o}from"../../../scene/model.js";import{ComponentSystem as i}from"../system.js";import{CollisionComponent as r}from"./component.js";import{CollisionComponentData as l}from"./data.js";import{Trigger as d}from"./trigger.js";const m=new e,c=new a,h=new a,p=new t,u=new n,f=["enabled","type","halfExtents","linearOffset","angularOffset","radius","axis","height","convexHull","asset","renderAsset","shape","model","render","checkVertexDuplicates"];class y{constructor(e){this.system=e}beforeInitialize(e,t){t.shape=null,t.model=new o,t.model.graph=new n}afterInitialize(e,t){this.recreatePhysicalShapes(e),e.data.initialized=!0}reset(e,t){this.beforeInitialize(e,t),this.afterInitialize(e,t)}recreatePhysicalShapes(e){const t=e.entity,a=e.data;if("undefined"!=typeof Ammo){t.trigger&&(t.trigger.destroy(),delete t.trigger),a.shape&&(e._compoundParent&&(e!==e._compoundParent&&this.system._removeCompoundChild(e._compoundParent,a.shape),e._compoundParent.entity.rigidbody&&e._compoundParent.entity.rigidbody.activate()),this.destroyShape(a)),a.shape=this.createPhysicalShape(e.entity,a);const s=!e._compoundParent;if("compound"!==a.type||e._compoundParent&&e!==e._compoundParent){if("compound"!==a.type&&!e.rigidbody){e._compoundParent=null;let a=t.parent;for(;a;){if(a.collision&&"compound"===a.collision.type){e._compoundParent=a.collision;break}a=a.parent}}}else e._compoundParent=e,t.forEach(this._addEachDescendant,e);e._compoundParent&&e!==e._compoundParent&&(s&&0===e._compoundParent.shape.getNumChildShapes()?this.system.recreatePhysicalShapes(e._compoundParent):(this.system.updateCompoundChildTransform(t,!0),e._compoundParent.entity.rigidbody&&e._compoundParent.entity.rigidbody.activate())),t.rigidbody?(t.rigidbody.disableSimulation(),t.rigidbody.createBody(),t.enabled&&t.rigidbody.enabled&&t.rigidbody.enableSimulation()):e._compoundParent||(t.trigger?t.trigger.initialize(a):t.trigger=new d(this.system.app,e,a))}}createPhysicalShape(e,t){}updateTransform(e,t,a,s){e.entity.trigger&&e.entity.trigger.updateTransform()}destroyShape(e){e.shape&&(Ammo.destroy(e.shape),e.shape=null)}beforeRemove(e,t){t.data.shape&&(t._compoundParent&&!t._compoundParent.entity._destroying&&(this.system._removeCompoundChild(t._compoundParent,t.data.shape),t._compoundParent.entity.rigidbody&&t._compoundParent.entity.rigidbody.activate()),t._compoundParent=null,this.destroyShape(t.data))}remove(e,t){e.rigidbody&&e.rigidbody.body&&e.rigidbody.disableSimulation(),e.trigger&&(e.trigger.destroy(),delete e.trigger)}clone(e,t){const a=this.system.store[e.getGuid()],s={enabled:a.data.enabled,type:a.data.type,halfExtents:[a.data.halfExtents.x,a.data.halfExtents.y,a.data.halfExtents.z],linearOffset:[a.data.linearOffset.x,a.data.linearOffset.y,a.data.linearOffset.z],angularOffset:[a.data.angularOffset.x,a.data.angularOffset.y,a.data.angularOffset.z,a.data.angularOffset.w],radius:a.data.radius,axis:a.data.axis,height:a.data.height,convexHull:a.data.convexHull,asset:a.data.asset,renderAsset:a.data.renderAsset,model:a.data.model,render:a.data.render,checkVertexDuplicates:a.data.checkVertexDuplicates};return this.system.addComponent(t,s)}}class g extends y{createPhysicalShape(e,t){if("undefined"!=typeof Ammo){const e=t.halfExtents,a=new Ammo.btVector3(e?e.x:.5,e?e.y:.5,e?e.z:.5),s=new Ammo.btBoxShape(a);return Ammo.destroy(a),s}}}class b extends y{createPhysicalShape(e,t){if("undefined"!=typeof Ammo)return new Ammo.btSphereShape(t.radius)}}class A extends y{createPhysicalShape(e,t){var a,s,n;const o=null!=(a=t.axis)?a:1,i=null!=(s=t.radius)?s:.5,r=Math.max((null!=(n=t.height)?n:2)-2*i,0);let l=null;if("undefined"!=typeof Ammo)switch(o){case 0:l=new Ammo.btCapsuleShapeX(i,r);break;case 1:l=new Ammo.btCapsuleShape(i,r);break;case 2:l=new Ammo.btCapsuleShapeZ(i,r)}return l}}class S extends y{createPhysicalShape(e,t){var a,s,n;const o=null!=(a=t.axis)?a:1,i=null!=(s=t.radius)?s:.5,r=null!=(n=t.height)?n:1;let l=null,d=null;if("undefined"!=typeof Ammo)switch(o){case 0:l=new Ammo.btVector3(.5*r,i,i),d=new Ammo.btCylinderShapeX(l);break;case 1:l=new Ammo.btVector3(i,.5*r,i),d=new Ammo.btCylinderShape(l);break;case 2:l=new Ammo.btVector3(i,i,.5*r),d=new Ammo.btCylinderShapeZ(l)}return l&&Ammo.destroy(l),d}}class x extends y{createPhysicalShape(e,t){var a,s,n;const o=null!=(a=t.axis)?a:1,i=null!=(s=t.radius)?s:.5,r=null!=(n=t.height)?n:1;let l=null;if("undefined"!=typeof Ammo)switch(o){case 0:l=new Ammo.btConeShapeX(i,r);break;case 1:l=new Ammo.btConeShape(i,r);break;case 2:l=new Ammo.btConeShapeZ(i,r)}return l}}class _ extends y{beforeInitialize(e,t){}createAmmoHull(e,t,a,s){const n=new Ammo.btConvexHullShape,o=new Ammo.btVector3,i=[];e.getPositions(i);for(let e=0;e<i.length;e+=3)o.setValue(i[e]*s.x,i[e+1]*s.y,i[e+2]*s.z),n.addPoint(o,!1);Ammo.destroy(o),n.recalcLocalAabb(),n.setMargin(.01),a.addChildShape(this.system._getNodeTransform(t),n)}createAmmoMesh(e,t,a,n,o=!0){const i=this.system;let r;if(i._triMeshCache[e.id])r=i._triMeshCache[e.id];else{const t=e.vertexBuffer,a=t.getFormat();let l,d;for(let e=0;e<a.elements.length;e++){const n=a.elements[e];if(n.name===s){d=new Float32Array(t.lock(),n.offset),l=n.stride/4;break}}const m=[];e.getIndices(m);const c=e.primitive[0].count/3,h=new Ammo.btVector3;let p,u,f;const y=e.primitive[0].base;r=new Ammo.btTriangleMesh,i._triMeshCache[e.id]=r;const g=new Map;r.getIndexedMeshArray().at(0).m_numTriangles=c;const b=n?n.x:1,A=n?n.y:1,S=n?n.z:1,x=e=>{const t=d[e*l]*b,a=d[e*l+1]*A,s=d[e*l+2]*S;let n;if(o){const e=`${t}:${a}:${s}`;if(n=g.get(e),void 0!==n)return n;h.setValue(t,a,s),n=r.findOrAddVertex(h,!1),g.set(e,n)}else h.setValue(t,a,s),n=r.findOrAddVertex(h,!1);return n};for(let e=0;e<c;e++)p=x(m[y+3*e]),u=x(m[y+3*e+1]),f=x(m[y+3*e+2]),r.addIndex(p),r.addIndex(u),r.addIndex(f);Ammo.destroy(h)}const l=new Ammo.btBvhTriangleMeshShape(r,!0);if(!n){const e=i._getNodeScaling(t);l.setLocalScaling(e),Ammo.destroy(e)}const d=i._getNodeTransform(t);a.addChildShape(d,l),Ammo.destroy(d)}createPhysicalShape(e,t){if("undefined"!=typeof Ammo&&(t.model||t.render)){const a=new Ammo.btCompoundShape,s=e.getWorldTransform().getScale();if(t.model){const e=t.model.meshInstances;for(let s=0;s<e.length;s++)this.createAmmoMesh(e[s].mesh,e[s].node,a,null,t.checkVertexDuplicates);const n=new Ammo.btVector3(s.x,s.y,s.z);a.setLocalScaling(n),Ammo.destroy(n)}else if(t.render){const e=t.render.meshes;for(let n=0;n<e.length;n++)t.convexHull?this.createAmmoHull(e[n],u,a,s):this.createAmmoMesh(e[n],u,a,s,t.checkVertexDuplicates)}return a}}recreatePhysicalShapes(e){const t=e.data;(t.renderAsset||t.asset)&&e.enabled&&e.entity.enabled?this.loadAsset(e,t.renderAsset||t.asset,t.renderAsset?"render":"model"):this.doRecreatePhysicalShape(e)}loadAsset(e,t,a){const s=e.data,n=this.system.app.assets,o=s[a],i=t=>{s[a]===o&&(s[a]=t.resource,this.doRecreatePhysicalShape(e))},r=e=>{e.ready((e=>{if(e.data.containerAsset){const t=n.get(e.data.containerAsset);t.loaded?i(e):(t.ready((()=>{i(e)})),n.load(t))}else i(e)})),n.load(e)},l=n.get(t);l?r(l):n.once(`add:${t}`,r)}doRecreatePhysicalShape(e){const t=e.entity,a=e.data;a.model||a.render?(this.destroyShape(a),a.shape=this.createPhysicalShape(t,a),t.rigidbody?(t.rigidbody.disableSimulation(),t.rigidbody.createBody(),t.enabled&&t.rigidbody.enabled&&t.rigidbody.enableSimulation()):t.trigger?t.trigger.initialize(a):t.trigger=new d(this.system.app,e,a)):(this.beforeRemove(t,e),this.remove(t,a))}updateTransform(e,t,a,s){if(e.shape){const t=e.entity.getWorldTransform().getScale(),a=e.shape.getLocalScaling();t.x===a.x()&&t.y===a.y()&&t.z===a.z()||this.doRecreatePhysicalShape(e)}super.updateTransform(e,t,a,s)}destroyShape(e){if(!e.shape)return;const t=e.shape.getNumChildShapes();for(let a=0;a<t;a++){const t=e.shape.getChildShape(a);Ammo.destroy(t)}Ammo.destroy(e.shape),e.shape=null}}class w extends y{createPhysicalShape(e,t){if("undefined"!=typeof Ammo)return new Ammo.btCompoundShape}_addEachDescendant(e){e.collision&&!e.rigidbody&&(e.collision._compoundParent=this,e!==this.entity&&e.collision.system.recreatePhysicalShapes(e.collision))}_updateEachDescendant(e){e.collision&&e.collision._compoundParent===this&&(e.collision._compoundParent=null,e===this.entity||e.rigidbody||e.collision.system.recreatePhysicalShapes(e.collision))}_updateEachDescendantTransform(e){e.collision&&e.collision._compoundParent===this.collision._compoundParent&&this.collision.system.updateCompoundChildTransform(e,!1)}}class P extends i{constructor(e){super(e),this.id="collision",this.ComponentType=r,this.DataType=l,this.schema=f,this.implementations={},this._triMeshCache={},this.on("beforeremove",this.onBeforeRemove,this),this.on("remove",this.onRemove,this)}initializeComponentData(e,s,n){const o={};for(let e=0,t=(n=["type","halfExtents","radius","axis","height","convexHull","shape","model","asset","render","renderAsset","enabled","linearOffset","angularOffset","checkVertexDuplicates"]).length;e<t;e++){const t=n[e];o[t]=s[t]}let i;if(s.hasOwnProperty("asset")?(i=n.indexOf("model"),-1!==i&&n.splice(i,1),i=n.indexOf("render"),-1!==i&&n.splice(i,1)):s.hasOwnProperty("model")&&(i=n.indexOf("asset"),-1!==i&&n.splice(i,1)),o.type||(o.type=e.data.type),e.data.type=o.type,Array.isArray(o.halfExtents)&&(o.halfExtents=new a(o.halfExtents)),Array.isArray(o.linearOffset)&&(o.linearOffset=new a(o.linearOffset)),Array.isArray(o.angularOffset)){const e=o.angularOffset;3===e.length?o.angularOffset=(new t).setFromEulerAngles(e[0],e[1],e[2]):o.angularOffset=new t(o.angularOffset)}const r=this._createImplementation(o.type);r.beforeInitialize(e,o),super.initializeComponentData(e,o,n),r.afterInitialize(e,o)}_createImplementation(e){if(void 0===this.implementations[e]){let t;switch(e){case"box":t=new g(this);break;case"sphere":t=new b(this);break;case"capsule":t=new A(this);break;case"cylinder":t=new S(this);break;case"cone":t=new x(this);break;case"mesh":t=new _(this);break;case"compound":t=new w(this)}this.implementations[e]=t}return this.implementations[e]}_getImplementation(e){return this.implementations[e.collision.data.type]}cloneComponent(e,t){return this._getImplementation(e).clone(e,t)}onBeforeRemove(e,t){this.implementations[t.data.type].beforeRemove(e,t),t.onBeforeRemove()}onRemove(e,t){this.implementations[t.type].remove(e,t)}updateCompoundChildTransform(e,t){const a=e.collision._compoundParent;if(a!==e.collision&&e.enabled&&e.collision.enabled&&(e._dirtyLocal||t)){const t=this._getNodeTransform(e,a.entity),s=a.getCompoundChildShapeIndex(e.collision.shape);null===s?a.shape.addChildShape(t,e.collision.data.shape):a.shape.updateChildTransform(s,t,!0),Ammo.destroy(t)}}_removeCompoundChild(e,t){if(0!==e.shape.getNumChildShapes())if(e.shape.removeChildShape)e.shape.removeChildShape(t);else{const a=e.getCompoundChildShapeIndex(t);null!==a&&e.shape.removeChildShapeByIndex(a)}}onTransformChanged(e,t,a,s){this.implementations[e.data.type].updateTransform(e,t,a,s)}changeType(e,t,a){this.implementations[t].beforeRemove(e.entity,e),this.implementations[t].remove(e.entity,e.data),this._createImplementation(a).reset(e,e.data)}recreatePhysicalShapes(e){this.implementations[e.data.type].recreatePhysicalShapes(e)}_calculateNodeRelativeTransform(e,t){if(e===t){const t=e.getWorldTransform().getScale();m.setScale(t.x,t.y,t.z)}else this._calculateNodeRelativeTransform(e.parent,t),m.mul(e.getLocalTransform())}_getNodeScaling(e){const t=e.getWorldTransform().getScale();return new Ammo.btVector3(t.x,t.y,t.z)}_getNodeTransform(e,t){let a,s;t?(this._calculateNodeRelativeTransform(e,t),a=c,s=p,m.getTranslation(a),s.setFromMat4(m)):(a=e.getPosition(),s=e.getRotation());const n=new Ammo.btQuaternion,o=new Ammo.btTransform;o.setIdentity();const i=o.getOrigin(),r=e.collision;if(r&&r._hasOffset){const e=r.data.linearOffset,t=r.data.angularOffset,o=h;p.copy(s).transformVector(e,o),o.add(a),p.copy(s).mul(t),i.setValue(o.x,o.y,o.z),n.setValue(p.x,p.y,p.z,p.w)}else i.setValue(a.x,a.y,a.z),n.setValue(s.x,s.y,s.z,s.w);return o.setRotation(n),Ammo.destroy(n),o}destroy(){for(const e in this._triMeshCache)Ammo.destroy(this._triMeshCache[e]);this._triMeshCache=null,super.destroy()}}export{P as CollisionComponentSystem};