@animech-public/playcanvas
Version:
PlayCanvas WebGL game engine
2 lines (1 loc) • 11.6 kB
JavaScript
import{ObjectPool as t}from"../../../core/object-pool.js";import{Vec3 as i}from"../../../core/math/vec3.js";import{Component as o}from"../component.js";import{ComponentSystem as s}from"../system.js";import{BODYFLAG_NORESPONSE_OBJECT as e}from"./constants.js";import{RigidBodyComponent as n}from"./component.js";import{RigidBodyComponentData as l}from"./data.js";let r,a;class c{constructor(t,i,o,s){this.entity=void 0,this.point=void 0,this.normal=void 0,this.hitFraction=void 0,this.entity=t,this.point=i,this.normal=o,this.hitFraction=s}}class h{constructor(t,o,s){this.a=void 0,this.b=void 0,this.impulse=void 0,this.localPointA=void 0,this.localPointB=void 0,this.pointA=void 0,this.pointB=void 0,this.normal=void 0,0!==arguments.length?(this.a=t,this.b=o,this.impulse=s.impulse,this.localPointA=s.localPoint,this.localPointB=s.localPointOther,this.pointA=s.point,this.pointB=s.pointOther,this.normal=s.normal):(this.a=null,this.b=null,this.impulse=0,this.localPointA=new i,this.localPointB=new i,this.pointA=new i,this.pointB=new i,this.normal=new i)}}class m{constructor(t=new i,o=new i,s=new i,e=new i,n=new i,l=0){this.localPoint=void 0,this.localPointOther=void 0,this.point=void 0,this.pointOther=void 0,this.normal=void 0,this.impulse=void 0,this.localPoint=t,this.localPointOther=o,this.point=s,this.pointOther=e,this.normal=n,this.impulse=l}}class d{constructor(t,i){this.other=void 0,this.contacts=void 0,this.other=t,this.contacts=i}}const g=["enabled"];class y extends s{constructor(t){super(t),this.maxSubSteps=10,this.fixedTimeStep=1/60,this.gravity=new i(0,-9.81,0),this._gravityFloat32=new Float32Array(3),this._dynamic=[],this._kinematic=[],this._triggers=[],this._compounds=[],this.id="rigidbody",this._stats=t.stats.frame,this.ComponentType=n,this.DataType=l,this.contactPointPool=null,this.contactResultPool=null,this.singleContactResultPool=null,this.schema=g,this.collisions={},this.frameCollisions={},this.on("beforeremove",this.onBeforeRemove,this)}onLibraryLoaded(){if("undefined"!=typeof Ammo){if(this.collisionConfiguration=new Ammo.btDefaultCollisionConfiguration,this.dispatcher=new Ammo.btCollisionDispatcher(this.collisionConfiguration),this.overlappingPairCache=new Ammo.btDbvtBroadphase,this.solver=new Ammo.btSequentialImpulseConstraintSolver,this.dynamicsWorld=new Ammo.btDiscreteDynamicsWorld(this.dispatcher,this.overlappingPairCache,this.solver,this.collisionConfiguration),this.dynamicsWorld.setInternalTickCallback){const t=Ammo.addFunction(this._checkForCollisions.bind(this),"vif");this.dynamicsWorld.setInternalTickCallback(t)}r=new Ammo.btVector3,a=new Ammo.btVector3,n.onLibraryLoaded(),this.contactPointPool=new t(m,1),this.contactResultPool=new t(d,1),this.singleContactResultPool=new t(h,1),this.app.systems.on("update",this.onUpdate,this)}else this.app.systems.off("update",this.onUpdate,this)}initializeComponentData(t,o,s){const e=["mass","linearDamping","angularDamping","linearFactor","angularFactor","friction","rollingFriction","restitution","type","group","mask"];for(const s of e)if(o.hasOwnProperty(s)){const e=o[s];Array.isArray(e)?t[s]=new i(e[0],e[1],e[2]):t[s]=e}super.initializeComponentData(t,o,["enabled"])}cloneComponent(t,i){const o=t.rigidbody,s={enabled:o.enabled,mass:o.mass,linearDamping:o.linearDamping,angularDamping:o.angularDamping,linearFactor:[o.linearFactor.x,o.linearFactor.y,o.linearFactor.z],angularFactor:[o.angularFactor.x,o.angularFactor.y,o.angularFactor.z],friction:o.friction,rollingFriction:o.rollingFriction,restitution:o.restitution,type:o.type,group:o.group,mask:o.mask};return this.addComponent(i,s)}onBeforeRemove(t,i){i.enabled&&(i.enabled=!1),i.body&&(this.destroyBody(i.body),i.body=null)}addBody(t,i,o){void 0!==i&&void 0!==o?this.dynamicsWorld.addRigidBody(t,i,o):this.dynamicsWorld.addRigidBody(t)}removeBody(t){this.dynamicsWorld.removeRigidBody(t)}createBody(t,i,o){const s=new Ammo.btVector3(0,0,0);0!==t&&i.calculateLocalInertia(t,s);const e=new Ammo.btDefaultMotionState(o),n=new Ammo.btRigidBodyConstructionInfo(t,e,i,s),l=new Ammo.btRigidBody(n);return Ammo.destroy(n),Ammo.destroy(s),l}destroyBody(t){const i=t.getMotionState();i&&Ammo.destroy(i),Ammo.destroy(t)}raycastFirst(t,o,s={}){if(s.filterTags||s.filterCallback)return s.sort=!0,this.raycastAll(t,o,s)[0]||null;let e=null;r.setValue(t.x,t.y,t.z),a.setValue(o.x,o.y,o.z);const n=new Ammo.ClosestRayResultCallback(r,a);if("number"==typeof s.filterCollisionGroup&&n.set_m_collisionFilterGroup(s.filterCollisionGroup),"number"==typeof s.filterCollisionMask&&n.set_m_collisionFilterMask(s.filterCollisionMask),this.dynamicsWorld.rayTest(r,a,n),n.hasHit()){const t=n.get_m_collisionObject(),o=Ammo.castObject(t,Ammo.btRigidBody);if(o){const t=n.get_m_hitPointWorld(),s=n.get_m_hitNormalWorld();e=new c(o.entity,new i(t.x(),t.y(),t.z()),new i(s.x(),s.y(),s.z()),n.get_m_closestHitFraction())}}return Ammo.destroy(n),e}raycastAll(t,o,s={}){const e=[];r.setValue(t.x,t.y,t.z),a.setValue(o.x,o.y,o.z);const n=new Ammo.AllHitsRayResultCallback(r,a);if("number"==typeof s.filterCollisionGroup&&n.set_m_collisionFilterGroup(s.filterCollisionGroup),"number"==typeof s.filterCollisionMask&&n.set_m_collisionFilterMask(s.filterCollisionMask),this.dynamicsWorld.rayTest(r,a,n),n.hasHit()){const t=n.get_m_collisionObjects(),o=n.get_m_hitPointWorld(),l=n.get_m_hitNormalWorld(),r=n.get_m_hitFractions(),a=t.size();for(let n=0;n<a;n++){const a=Ammo.castObject(t.at(n),Ammo.btRigidBody);if(a&&a.entity){if(s.filterTags&&!a.entity.tags.has(...s.filterTags)||s.filterCallback&&!s.filterCallback(a.entity))continue;const t=o.at(n),h=l.at(n),m=new c(a.entity,new i(t.x(),t.y(),t.z()),new i(h.x(),h.y(),h.z()),r.at(n));e.push(m)}}s.sort&&e.sort(((t,i)=>t.hitFraction-i.hitFraction))}return Ammo.destroy(n),e}_storeCollision(t,i){let o=!1;const s=t.getGuid();return this.collisions[s]=this.collisions[s]||{others:[],entity:t},this.collisions[s].others.indexOf(i)<0&&(this.collisions[s].others.push(i),o=!0),this.frameCollisions[s]=this.frameCollisions[s]||{others:[],entity:t},this.frameCollisions[s].others.push(i),o}_createContactPointFromAmmo(t){const i=t.get_m_localPointA(),o=t.get_m_localPointB(),s=t.getPositionWorldOnA(),e=t.getPositionWorldOnB(),n=t.get_m_normalWorldOnB(),l=this.contactPointPool.allocate();return l.localPoint.set(i.x(),i.y(),i.z()),l.localPointOther.set(o.x(),o.y(),o.z()),l.point.set(s.x(),s.y(),s.z()),l.pointOther.set(e.x(),e.y(),e.z()),l.normal.set(n.x(),n.y(),n.z()),l.impulse=t.getAppliedImpulse(),l}_createReverseContactPointFromAmmo(t){const i=t.get_m_localPointA(),o=t.get_m_localPointB(),s=t.getPositionWorldOnA(),e=t.getPositionWorldOnB(),n=t.get_m_normalWorldOnB(),l=this.contactPointPool.allocate();return l.localPointOther.set(i.x(),i.y(),i.z()),l.localPoint.set(o.x(),o.y(),o.z()),l.pointOther.set(s.x(),s.y(),s.z()),l.point.set(e.x(),e.y(),e.z()),l.normal.set(n.x(),n.y(),n.z()),l.impulse=t.getAppliedImpulse(),l}_createSingleContactResult(t,i,o){const s=this.singleContactResultPool.allocate();return s.a=t,s.b=i,s.localPointA=o.localPoint,s.localPointB=o.localPointOther,s.pointA=o.point,s.pointB=o.pointOther,s.normal=o.normal,s.impulse=o.impulse,s}_createContactResult(t,i){const o=this.contactResultPool.allocate();return o.other=t,o.contacts=i,o}_cleanOldCollisions(){for(const t in this.collisions)if(this.collisions.hasOwnProperty(t)){const i=this.frameCollisions[t],o=this.collisions[t],s=o.entity,e=s.collision,n=s.rigidbody,l=o.others;let r=l.length;for(;r--;){const t=l[r];(!i||i.others.indexOf(t)<0)&&(l.splice(r,1),s.trigger?(e&&e.fire("triggerleave",t),t.rigidbody&&t.rigidbody.fire("triggerleave",s)):t.trigger||(n&&n.fire("collisionend",t),e&&e.fire("collisionend",t)))}0===l.length&&delete this.collisions[t]}}_hasContactEvent(t){const i=t.collision;if(i&&(i.hasEvent("collisionstart")||i.hasEvent("collisionend")||i.hasEvent("contact")))return!0;const o=t.rigidbody;return o&&(o.hasEvent("collisionstart")||o.hasEvent("collisionend")||o.hasEvent("contact"))}_checkForCollisions(t,i){const o=Ammo.wrapPointer(t,Ammo.btDynamicsWorld).getDispatcher(),s=o.getNumManifolds();this.frameCollisions={};for(let t=0;t<s;t++){const i=o.getManifoldByIndexInternal(t),s=i.getBody0(),n=i.getBody1(),l=Ammo.castObject(s,Ammo.btRigidBody),r=Ammo.castObject(n,Ammo.btRigidBody),a=l.entity,c=r.entity;if(!a||!c)continue;const h=l.getCollisionFlags(),m=r.getCollisionFlags(),d=i.getNumContacts(),g=[],y=[];let p;if(d>0)if(h&e||m&e){const t=a.collision&&(a.collision.hasEvent("triggerenter")||a.collision.hasEvent("triggerleave")),i=c.collision&&(c.collision.hasEvent("triggerenter")||c.collision.hasEvent("triggerleave")),o=a.rigidbody&&(a.rigidbody.hasEvent("triggerenter")||a.rigidbody.hasEvent("triggerleave")),s=c.rigidbody&&(c.rigidbody.hasEvent("triggerenter")||c.rigidbody.hasEvent("triggerleave"));t&&(p=this._storeCollision(a,c),!p||m&e||a.collision.fire("triggerenter",c)),i&&(p=this._storeCollision(c,a),!p||h&e||c.collision.fire("triggerenter",a)),o&&(p||(p=this._storeCollision(c,a)),p&&a.rigidbody.fire("triggerenter",c)),s&&(p||(p=this._storeCollision(a,c)),p&&c.rigidbody.fire("triggerenter",a))}else{const t=this._hasContactEvent(a),o=this._hasContactEvent(c),s=this.hasEvent("contact");if(s||t||o){for(let e=0;e<d;e++){const n=i.getContactPoint(e),l=this._createContactPointFromAmmo(n);if(t||o){g.push(l);const t=this._createReverseContactPointFromAmmo(n);y.push(t)}if(s){const t=this._createSingleContactResult(a,c,l);this.fire("contact",t)}}if(t){const t=this._createContactResult(c,g);p=this._storeCollision(a,c),a.collision&&(a.collision.fire("contact",t),p&&a.collision.fire("collisionstart",t)),a.rigidbody&&(a.rigidbody.fire("contact",t),p&&a.rigidbody.fire("collisionstart",t))}if(o){const t=this._createContactResult(a,y);p=this._storeCollision(c,a),c.collision&&(c.collision.fire("contact",t),p&&c.collision.fire("collisionstart",t)),c.rigidbody&&(c.rigidbody.fire("contact",t),p&&c.rigidbody.fire("collisionstart",t))}}}}this._cleanOldCollisions(),this.contactPointPool.freeAll(),this.contactResultPool.freeAll(),this.singleContactResultPool.freeAll()}onUpdate(t){let i,o;this._gravityFloat32[0]=this.gravity.x,this._gravityFloat32[1]=this.gravity.y,this._gravityFloat32[2]=this.gravity.z;const s=this.dynamicsWorld.getGravity();s.x()===this._gravityFloat32[0]&&s.y()===this._gravityFloat32[1]&&s.z()===this._gravityFloat32[2]||(s.setValue(this.gravity.x,this.gravity.y,this.gravity.z),this.dynamicsWorld.setGravity(s));const e=this._triggers;for(i=0,o=e.length;i<o;i++)e[i].updateTransform();const n=this._compounds;for(i=0,o=n.length;i<o;i++)n[i]._updateCompound();const l=this._kinematic;for(i=0,o=l.length;i<o;i++)l[i]._updateKinematic();this.dynamicsWorld.stepSimulation(t,this.maxSubSteps,this.fixedTimeStep);const r=this._dynamic;for(i=0,o=r.length;i<o;i++)r[i]._updateDynamic();this.dynamicsWorld.setInternalTickCallback||this._checkForCollisions(Ammo.getPointer(this.dynamicsWorld),t)}destroy(){super.destroy(),this.app.systems.off("update",this.onUpdate,this),"undefined"!=typeof Ammo&&(Ammo.destroy(this.dynamicsWorld),Ammo.destroy(this.solver),Ammo.destroy(this.overlappingPairCache),Ammo.destroy(this.dispatcher),Ammo.destroy(this.collisionConfiguration),Ammo.destroy(r),Ammo.destroy(a),this.dynamicsWorld=null,this.solver=null,this.overlappingPairCache=null,this.dispatcher=null,this.collisionConfiguration=null,r=null,a=null,n.onAppDestroy())}}y.EVENT_CONTACT="contact",o._buildAccessors(n.prototype,g);export{m as ContactPoint,d as ContactResult,c as RaycastResult,y as RigidBodyComponentSystem,h as SingleContactResult};