babylon.physicsHelper.ts 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719
  1. module BABYLON {
  2. export class PhysicsHelper {
  3. private _scene: Scene;
  4. private _physicsEngine: Nullable<PhysicsEngine>;
  5. constructor(scene: Scene) {
  6. this._scene = scene;
  7. this._physicsEngine = this._scene.getPhysicsEngine();
  8. if (!this._physicsEngine) {
  9. Tools.Warn('Physics engine not enabled. Please enable the physics before you can use the methods.');
  10. }
  11. }
  12. /**
  13. * @param {Vector3} origin the origin of the explosion
  14. * @param {number} radius the explosion radius
  15. * @param {number} strength the explosion strength
  16. * @param {PhysicsRadialImpulseFalloff} falloff possible options: Constant & Linear. Defaults to Constant
  17. */
  18. public applyRadialExplosionImpulse(origin: Vector3, radius: number, strength: number, falloff: PhysicsRadialImpulseFalloff = PhysicsRadialImpulseFalloff.Constant): Nullable<PhysicsRadialExplosionEvent> {
  19. if (!this._physicsEngine) {
  20. Tools.Warn('Physics engine not enabled. Please enable the physics before you call this method.');
  21. return null;
  22. }
  23. var impostors = this._physicsEngine.getImpostors();
  24. if (impostors.length === 0) {
  25. return null;
  26. }
  27. var event = new PhysicsRadialExplosionEvent(this._scene);
  28. impostors.forEach(impostor => {
  29. var impostorForceAndContactPoint = event.getImpostorForceAndContactPoint(impostor, origin, radius, strength, falloff);
  30. if (!impostorForceAndContactPoint) {
  31. return;
  32. }
  33. impostor.applyImpulse(impostorForceAndContactPoint.force, impostorForceAndContactPoint.contactPoint);
  34. });
  35. event.dispose(false);
  36. return event;
  37. }
  38. /**
  39. * @param {Vector3} origin the origin of the explosion
  40. * @param {number} radius the explosion radius
  41. * @param {number} strength the explosion strength
  42. * @param {PhysicsRadialImpulseFalloff} falloff possible options: Constant & Linear. Defaults to Constant
  43. */
  44. public applyRadialExplosionForce(origin: Vector3, radius: number, strength: number, falloff: PhysicsRadialImpulseFalloff = PhysicsRadialImpulseFalloff.Constant): Nullable<PhysicsRadialExplosionEvent> {
  45. if (!this._physicsEngine) {
  46. Tools.Warn('Physics engine not enabled. Please enable the physics before you call the PhysicsHelper.');
  47. return null;
  48. }
  49. var impostors = this._physicsEngine.getImpostors();
  50. if (impostors.length === 0) {
  51. return null;
  52. }
  53. var event = new PhysicsRadialExplosionEvent(this._scene);
  54. impostors.forEach(impostor => {
  55. var impostorForceAndContactPoint = event.getImpostorForceAndContactPoint(impostor, origin, radius, strength, falloff);
  56. if (!impostorForceAndContactPoint) {
  57. return;
  58. }
  59. impostor.applyForce(impostorForceAndContactPoint.force, impostorForceAndContactPoint.contactPoint);
  60. });
  61. event.dispose(false);
  62. return event;
  63. }
  64. /**
  65. * @param {Vector3} origin the origin of the explosion
  66. * @param {number} radius the explosion radius
  67. * @param {number} strength the explosion strength
  68. * @param {PhysicsRadialImpulseFalloff} falloff possible options: Constant & Linear. Defaults to Constant
  69. */
  70. public gravitationalField(origin: Vector3, radius: number, strength: number, falloff: PhysicsRadialImpulseFalloff = PhysicsRadialImpulseFalloff.Constant): Nullable<PhysicsGravitationalFieldEvent> {
  71. if (!this._physicsEngine) {
  72. Tools.Warn('Physics engine not enabled. Please enable the physics before you call the PhysicsHelper.');
  73. return null;
  74. }
  75. var impostors = this._physicsEngine.getImpostors();
  76. if (impostors.length === 0) {
  77. return null;
  78. }
  79. var event = new PhysicsGravitationalFieldEvent(this, this._scene, origin, radius, strength, falloff);
  80. event.dispose(false);
  81. return event;
  82. }
  83. /**
  84. * @param {Vector3} origin the origin of the updraft
  85. * @param {number} radius the radius of the updraft
  86. * @param {number} strength the strength of the updraft
  87. * @param {number} height the height of the updraft
  88. * @param {PhysicsUpdraftMode} updraftMode possible options: Center & Perpendicular. Defaults to Center
  89. */
  90. public updraft(origin: Vector3, radius: number, strength: number, height: number, updraftMode: PhysicsUpdraftMode = PhysicsUpdraftMode.Center): Nullable<PhysicsUpdraftEvent> {
  91. if (!this._physicsEngine) {
  92. Tools.Warn('Physics engine not enabled. Please enable the physics before you call the PhysicsHelper.');
  93. return null;
  94. }
  95. if (this._physicsEngine.getImpostors().length === 0) {
  96. return null;
  97. }
  98. var event = new PhysicsUpdraftEvent(this._physicsEngine, this._scene, origin, radius, strength, height, updraftMode);
  99. event.dispose(false);
  100. return event;
  101. }
  102. /**
  103. * @param {Vector3} origin the origin of the vortex
  104. * @param {number} radius the radius of the vortex
  105. * @param {number} strength the strength of the vortex
  106. * @param {number} height the height of the vortex
  107. * @param {number} angularVelocity the angularVelocity for the vortex
  108. */
  109. public vortex(origin: Vector3, radius: number, strength: number, height: number, angularVelocity: number): Nullable<PhysicsVortexEvent> {
  110. if (!this._physicsEngine) {
  111. Tools.Warn('Physics engine not enabled. Please enable the physics before you call the PhysicsHelper.');
  112. return null;
  113. }
  114. if (this._physicsEngine.getImpostors().length === 0) {
  115. return null;
  116. }
  117. var event = new PhysicsVortexEvent(this._physicsEngine, this._scene, origin, radius, strength, height, angularVelocity);
  118. event.dispose(false);
  119. return event;
  120. }
  121. }
  122. /***** Radial explosion *****/
  123. export class PhysicsRadialExplosionEvent {
  124. private _scene: Scene;
  125. private _sphere: Mesh; // create a sphere, so we can get the intersecting meshes inside
  126. private _sphereOptions: { segments: number, diameter: number } = { segments: 32, diameter: 1 }; // TODO: make configurable
  127. private _rays: Array<Ray> = [];
  128. private _dataFetched: boolean = false; // check if the data has been fetched. If not, do cleanup
  129. constructor(scene: Scene) {
  130. this._scene = scene;
  131. }
  132. /**
  133. * Returns the data related to the radial explosion event (sphere & rays).
  134. * @returns {PhysicsRadialExplosionEventData}
  135. */
  136. public getData(): PhysicsRadialExplosionEventData {
  137. this._dataFetched = true;
  138. return {
  139. sphere: this._sphere,
  140. rays: this._rays,
  141. };
  142. }
  143. /**
  144. * Returns the force and contact point of the impostor or false, if the impostor is not affected by the force/impulse.
  145. * @param impostor
  146. * @param {Vector3} origin the origin of the explosion
  147. * @param {number} radius the explosion radius
  148. * @param {number} strength the explosion strength
  149. * @param {PhysicsRadialImpulseFalloff} falloff possible options: Constant & Linear
  150. * @returns {Nullable<PhysicsForceAndContactPoint>}
  151. */
  152. public getImpostorForceAndContactPoint(impostor: PhysicsImpostor, origin: Vector3, radius: number, strength: number, falloff: PhysicsRadialImpulseFalloff): Nullable<PhysicsForceAndContactPoint> {
  153. if (impostor.mass === 0) {
  154. return null;
  155. }
  156. if (!this._intersectsWithSphere(impostor, origin, radius)) {
  157. return null;
  158. }
  159. var impostorObject = (<Mesh>impostor.object);
  160. var impostorObjectCenter = impostor.getObjectCenter();
  161. var direction = impostorObjectCenter.subtract(origin);
  162. var ray = new Ray(origin, direction, radius);
  163. this._rays.push(ray);
  164. var hit = ray.intersectsMesh(impostorObject);
  165. var contactPoint = hit.pickedPoint;
  166. if (!contactPoint) {
  167. return null;
  168. }
  169. var distanceFromOrigin = Vector3.Distance(origin, contactPoint);
  170. if (distanceFromOrigin > radius) {
  171. return null;
  172. }
  173. var multiplier = falloff === PhysicsRadialImpulseFalloff.Constant
  174. ? strength
  175. : strength * (1 - (distanceFromOrigin / radius));
  176. var force = direction.multiplyByFloats(multiplier, multiplier, multiplier);
  177. return { force: force, contactPoint: contactPoint };
  178. }
  179. /**
  180. * Disposes the sphere.
  181. * @param {bolean} force
  182. */
  183. public dispose(force: boolean = true) {
  184. if (force) {
  185. this._sphere.dispose();
  186. } else {
  187. setTimeout(() => {
  188. if (!this._dataFetched) {
  189. this._sphere.dispose();
  190. }
  191. }, 0);
  192. }
  193. }
  194. /*** Helpers ***/
  195. private _prepareSphere(): void {
  196. if (!this._sphere) {
  197. this._sphere = MeshBuilder.CreateSphere("radialExplosionEventSphere", this._sphereOptions, this._scene);
  198. this._sphere.isVisible = false;
  199. }
  200. }
  201. private _intersectsWithSphere(impostor: PhysicsImpostor, origin: Vector3, radius: number): boolean {
  202. var impostorObject = <Mesh>impostor.object;
  203. this._prepareSphere();
  204. this._sphere.position = origin;
  205. this._sphere.scaling = new Vector3(radius * 2, radius * 2, radius * 2);
  206. this._sphere._updateBoundingInfo();
  207. this._sphere.computeWorldMatrix(true);
  208. return this._sphere.intersectsMesh(impostorObject, true);
  209. }
  210. }
  211. /***** Gravitational Field *****/
  212. export class PhysicsGravitationalFieldEvent {
  213. private _physicsHelper: PhysicsHelper;
  214. private _scene: Scene;
  215. private _origin: Vector3;
  216. private _radius: number;
  217. private _strength: number;
  218. private _falloff: PhysicsRadialImpulseFalloff;
  219. private _tickCallback: any;
  220. private _sphere: Mesh;
  221. private _dataFetched: boolean = false; // check if the has been fetched the data. If not, do cleanup
  222. constructor(physicsHelper: PhysicsHelper, scene: Scene, origin: Vector3, radius: number, strength: number, falloff: PhysicsRadialImpulseFalloff = PhysicsRadialImpulseFalloff.Constant) {
  223. this._physicsHelper = physicsHelper;
  224. this._scene = scene;
  225. this._origin = origin;
  226. this._radius = radius;
  227. this._strength = strength;
  228. this._falloff = falloff;
  229. this._tickCallback = this._tick.bind(this);
  230. }
  231. /**
  232. * Returns the data related to the gravitational field event (sphere).
  233. * @returns {PhysicsGravitationalFieldEventData}
  234. */
  235. public getData(): PhysicsGravitationalFieldEventData {
  236. this._dataFetched = true;
  237. return {
  238. sphere: this._sphere,
  239. };
  240. }
  241. /**
  242. * Enables the gravitational field.
  243. */
  244. public enable() {
  245. this._tickCallback.call(this);
  246. this._scene.registerBeforeRender(this._tickCallback);
  247. }
  248. /**
  249. * Disables the gravitational field.
  250. */
  251. public disable() {
  252. this._scene.unregisterBeforeRender(this._tickCallback);
  253. }
  254. /**
  255. * Disposes the sphere.
  256. * @param {bolean} force
  257. */
  258. public dispose(force: boolean = true) {
  259. if (force) {
  260. this._sphere.dispose();
  261. } else {
  262. setTimeout(() => {
  263. if (!this._dataFetched) {
  264. this._sphere.dispose();
  265. }
  266. }, 0);
  267. }
  268. }
  269. private _tick() {
  270. // Since the params won't change, we fetch the event only once
  271. if (this._sphere) {
  272. this._physicsHelper.applyRadialExplosionForce(this._origin, this._radius, this._strength * -1, this._falloff);
  273. } else {
  274. var radialExplosionEvent = this._physicsHelper.applyRadialExplosionForce(this._origin, this._radius, this._strength * -1, this._falloff);
  275. if (radialExplosionEvent) {
  276. this._sphere = <Mesh>radialExplosionEvent.getData().sphere.clone('radialExplosionEventSphereClone');
  277. }
  278. }
  279. }
  280. }
  281. /***** Updraft *****/
  282. export class PhysicsUpdraftEvent {
  283. private _physicsEngine: PhysicsEngine;
  284. private _scene: Scene;
  285. private _origin: Vector3;
  286. private _originTop: Vector3 = Vector3.Zero(); // the most upper part of the cylinder
  287. private _originDirection: Vector3 = Vector3.Zero(); // used if the updraftMode is perpendicular
  288. private _radius: number;
  289. private _strength: number;
  290. private _height: number;
  291. private _updraftMode: PhysicsUpdraftMode;
  292. private _tickCallback: any;
  293. private _cylinder: Mesh;
  294. private _cylinderPosition: Vector3 = Vector3.Zero(); // to keep the cylinders position, because normally the origin is in the center and not on the bottom
  295. private _dataFetched: boolean = false; // check if the has been fetched the data. If not, do cleanup
  296. constructor(physicsEngine: PhysicsEngine, scene: Scene, origin: Vector3, radius: number, strength: number, height: number, updraftMode: PhysicsUpdraftMode) {
  297. this._physicsEngine = physicsEngine;
  298. this._scene = scene;
  299. this._origin = origin;
  300. this._radius = radius;
  301. this._strength = strength;
  302. this._height = height;
  303. this._updraftMode = updraftMode;
  304. this._origin.addToRef(new Vector3(0, this._height / 2, 0), this._cylinderPosition);
  305. this._origin.addToRef(new Vector3(0, this._height, 0), this._originTop);
  306. if (this._updraftMode === PhysicsUpdraftMode.Perpendicular) {
  307. this._originDirection = this._origin.subtract(this._originTop).normalize();
  308. }
  309. this._tickCallback = this._tick.bind(this);
  310. }
  311. /**
  312. * Returns the data related to the updraft event (cylinder).
  313. * @returns {PhysicsUpdraftEventData}
  314. */
  315. public getData(): PhysicsUpdraftEventData {
  316. this._dataFetched = true;
  317. return {
  318. cylinder: this._cylinder,
  319. };
  320. }
  321. /**
  322. * Enables the updraft.
  323. */
  324. public enable() {
  325. this._tickCallback.call(this);
  326. this._scene.registerBeforeRender(this._tickCallback);
  327. }
  328. /**
  329. * Disables the cortex.
  330. */
  331. public disable() {
  332. this._scene.unregisterBeforeRender(this._tickCallback);
  333. }
  334. /**
  335. * Disposes the sphere.
  336. * @param {bolean} force
  337. */
  338. public dispose(force: boolean = true) {
  339. if (force) {
  340. this._cylinder.dispose();
  341. } else {
  342. setTimeout(() => {
  343. if (!this._dataFetched) {
  344. this._cylinder.dispose();
  345. }
  346. }, 0);
  347. }
  348. }
  349. private getImpostorForceAndContactPoint(impostor: PhysicsImpostor): Nullable<PhysicsForceAndContactPoint> {
  350. if (impostor.mass === 0) {
  351. return null;
  352. }
  353. if (!this._intersectsWithCylinder(impostor)) {
  354. return null;
  355. }
  356. var impostorObjectCenter = impostor.getObjectCenter();
  357. if (this._updraftMode === PhysicsUpdraftMode.Perpendicular) {
  358. var direction = this._originDirection;
  359. } else {
  360. var direction = impostorObjectCenter.subtract(this._originTop);
  361. }
  362. var multiplier = this._strength * -1;
  363. var force = direction.multiplyByFloats(multiplier, multiplier, multiplier);
  364. return { force: force, contactPoint: impostorObjectCenter };
  365. }
  366. private _tick() {
  367. this._physicsEngine.getImpostors().forEach(impostor => {
  368. var impostorForceAndContactPoint = this.getImpostorForceAndContactPoint(impostor);
  369. if (!impostorForceAndContactPoint) {
  370. return;
  371. }
  372. impostor.applyForce(impostorForceAndContactPoint.force, impostorForceAndContactPoint.contactPoint);
  373. });
  374. }
  375. /*** Helpers ***/
  376. private _prepareCylinder(): void {
  377. if (!this._cylinder) {
  378. this._cylinder = MeshBuilder.CreateCylinder("updraftEventCylinder", {
  379. height: this._height,
  380. diameter: this._radius * 2,
  381. }, this._scene);
  382. this._cylinder.isVisible = false;
  383. }
  384. }
  385. private _intersectsWithCylinder(impostor: PhysicsImpostor): boolean {
  386. var impostorObject = <Mesh>impostor.object;
  387. this._prepareCylinder();
  388. this._cylinder.position = this._cylinderPosition;
  389. return this._cylinder.intersectsMesh(impostorObject, true);
  390. }
  391. }
  392. /***** Vortex *****/
  393. export class PhysicsVortexEvent {
  394. private _physicsEngine: PhysicsEngine;
  395. private _scene: Scene;
  396. private _origin: Vector3;
  397. private _originTop: Vector3 = Vector3.Zero(); // the most upper part of the cylinder
  398. private _radius: number;
  399. private _strength: number;
  400. private _height: number;
  401. private _angularVelocity: number;
  402. private _centripetalForceThreshold: number = 0.6;
  403. private _updraftMultiplier: number = 0.02;
  404. private _tickCallback: any;
  405. private _cylinder: Mesh;
  406. private _cylinderPosition: Vector3 = Vector3.Zero(); // to keep the cylinders position, because normally the origin is in the center and not on the bottom
  407. private _dataFetched: boolean = false; // check if the has been fetched the data. If not, do cleanup
  408. constructor(physicsEngine: PhysicsEngine, scene: Scene, origin: Vector3, radius: number, strength: number, height: number, angularVelocity: number) {
  409. this._physicsEngine = physicsEngine;
  410. this._scene = scene;
  411. this._origin = origin;
  412. this._radius = radius;
  413. this._strength = strength;
  414. this._height = height;
  415. this._angularVelocity = angularVelocity;
  416. this._origin.addToRef(new Vector3(0, this._height / 2, 0), this._cylinderPosition);
  417. this._origin.addToRef(new Vector3(0, this._height, 0), this._originTop);
  418. this._tickCallback = this._tick.bind(this);
  419. }
  420. /**
  421. * Returns the data related to the vortex event (cylinder).
  422. * @returns {PhysicsVortexEventData}
  423. */
  424. public getData(): PhysicsVortexEventData {
  425. this._dataFetched = true;
  426. return {
  427. cylinder: this._cylinder,
  428. };
  429. }
  430. /**
  431. * Enables the vortex.
  432. */
  433. public enable() {
  434. this._tickCallback.call(this);
  435. this._scene.registerBeforeRender(this._tickCallback);
  436. }
  437. /**
  438. * Disables the cortex.
  439. */
  440. public disable() {
  441. this._scene.unregisterBeforeRender(this._tickCallback);
  442. }
  443. /**
  444. * Disposes the sphere.
  445. * @param {bolean} force
  446. */
  447. public dispose(force: boolean = true) {
  448. if (force) {
  449. this._cylinder.dispose();
  450. } else {
  451. setTimeout(() => {
  452. if (!this._dataFetched) {
  453. this._cylinder.dispose();
  454. }
  455. }, 0);
  456. }
  457. }
  458. private getImpostorForceAndContactPoint(impostor: PhysicsImpostor): Nullable<PhysicsForceAndContactPoint> {
  459. if (impostor.mass === 0) {
  460. return null;
  461. }
  462. if (!this._intersectsWithCylinder(impostor)) {
  463. return null;
  464. }
  465. var impostorObject = <Mesh>impostor.object;
  466. var impostorObjectCenter = impostor.getObjectCenter();
  467. var originOnPlane = new Vector3(this._origin.x, impostorObjectCenter.y, this._origin.z); // the distance to the origin as if both objects were on a plane (Y-axis)
  468. var originToImpostorDirection = impostorObjectCenter.subtract(originOnPlane);
  469. var ray = new Ray(originOnPlane, originToImpostorDirection, this._radius);
  470. var hit = ray.intersectsMesh(impostorObject);
  471. var contactPoint = hit.pickedPoint;
  472. if (!contactPoint) {
  473. return null;
  474. }
  475. var absoluteDistanceFromOrigin = hit.distance / this._radius;
  476. var perpendicularDirection = Vector3.Cross(originOnPlane, impostorObjectCenter).normalize();
  477. var directionToOrigin = contactPoint.normalize();
  478. if (absoluteDistanceFromOrigin > this._centripetalForceThreshold) {
  479. directionToOrigin = directionToOrigin.negate();
  480. }
  481. var forceX = (perpendicularDirection.x + directionToOrigin.x) / 2;
  482. var forceY = this._originTop.y * this._updraftMultiplier;
  483. var forceZ = (perpendicularDirection.z + directionToOrigin.z) / 2;
  484. // TODO: find a more physically based solution
  485. if (absoluteDistanceFromOrigin > 0.8) {
  486. forceX = directionToOrigin.x * this._strength / 8;
  487. forceY = directionToOrigin.y * this._updraftMultiplier;
  488. forceZ = directionToOrigin.z * this._strength / 8;
  489. }
  490. // TODO: implement angular velocity
  491. var force = new Vector3(forceX, forceY, forceZ);
  492. force = force.multiplyByFloats(this._strength, this._strength, this._strength);
  493. /*
  494. // DEBUG
  495. // Force - Red
  496. var debugForceRay = new Ray(impostorObjectCenter, force, 0.2);
  497. var debugForceRayHelper = new BABYLON.RayHelper(debugForceRay);
  498. debugForceRayHelper.show(this._scene, new BABYLON.Color3(1, 0, 0));
  499. setTimeout(function (debugForceRayHelper) { debugForceRayHelper.dispose() }, 20, debugForceRayHelper);
  500. // Direction to origin - Blue
  501. var debugDirectionRay = new Ray(impostorObjectCenter, directionToOrigin, 2.5);
  502. var debugDirectionRayHelper = new BABYLON.RayHelper(debugDirectionRay);
  503. debugDirectionRayHelper.show(this._scene, new BABYLON.Color3(0, 0, 1));
  504. setTimeout(function (debugDirectionRayHelper) { debugDirectionRayHelper.dispose() }, 20, debugDirectionRayHelper);
  505. // Perpendicular direction - Green
  506. var debugPerpendicularDirectionRay = new Ray(impostorObjectCenter, perpendicularDirection, 2.5);
  507. var debugPerpendicularDirectionRayHelper = new BABYLON.RayHelper(debugPerpendicularDirectionRay);
  508. debugPerpendicularDirectionRayHelper.show(this._scene, new BABYLON.Color3(0, 1, 0));
  509. setTimeout(function (debugPerpendicularDirectionRayHelper) { debugPerpendicularDirectionRayHelper.dispose() }, 20, debugPerpendicularDirectionRayHelper);
  510. // DEBUG /END
  511. */
  512. return { force: force, contactPoint: impostorObjectCenter };
  513. }
  514. private _tick() {
  515. this._physicsEngine.getImpostors().forEach(impostor => {
  516. var impostorForceAndContactPoint = this.getImpostorForceAndContactPoint(impostor);
  517. if (!impostorForceAndContactPoint) {
  518. return;
  519. }
  520. impostor.applyForce(impostorForceAndContactPoint.force, impostorForceAndContactPoint.contactPoint);
  521. });
  522. }
  523. /*** Helpers ***/
  524. private _prepareCylinder(): void {
  525. if (!this._cylinder) {
  526. this._cylinder = MeshBuilder.CreateCylinder("vortexEventCylinder", {
  527. height: this._height,
  528. diameter: this._radius * 2,
  529. }, this._scene);
  530. this._cylinder.isVisible = false;
  531. }
  532. }
  533. private _intersectsWithCylinder(impostor: PhysicsImpostor): boolean {
  534. var impostorObject = <Mesh>impostor.object;
  535. this._prepareCylinder();
  536. this._cylinder.position = this._cylinderPosition;
  537. return this._cylinder.intersectsMesh(impostorObject, true);
  538. }
  539. }
  540. /***** Enums *****/
  541. /**
  542. * The strenght of the force in correspondence to the distance of the affected object
  543. */
  544. export enum PhysicsRadialImpulseFalloff {
  545. Constant, // impulse is constant in strength across it's whole radius
  546. Linear // impulse gets weaker if it's further from the origin
  547. }
  548. /**
  549. * The strenght of the force in correspondence to the distance of the affected object
  550. */
  551. export enum PhysicsUpdraftMode {
  552. Center, // the upstream forces will pull towards the top center of the cylinder
  553. Perpendicular // once a impostor is inside the cylinder, it will shoot out perpendicular from the ground of the cylinder
  554. }
  555. /***** Data interfaces *****/
  556. export interface PhysicsForceAndContactPoint {
  557. force: Vector3;
  558. contactPoint: Vector3;
  559. }
  560. export interface PhysicsRadialExplosionEventData {
  561. sphere: Mesh;
  562. rays: Array<Ray>;
  563. }
  564. export interface PhysicsGravitationalFieldEventData {
  565. sphere: Mesh;
  566. }
  567. export interface PhysicsUpdraftEventData {
  568. cylinder: Mesh;
  569. }
  570. export interface PhysicsVortexEventData {
  571. cylinder: Mesh;
  572. }
  573. }